summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.gitreview4
-rwxr-xr-xVERSION2
-rwxr-xr-xdoc/AnalyticsConnect.py195
-rwxr-xr-xdoc/AnalyticsWebReport.py30
-rw-r--r--doc/README17
-rwxr-xr-xdoc/TRexDataAnalysis.py171
-rwxr-xr-xdoc/b2
-rw-r--r--doc/images/port_normal_mode.pngbin0 -> 79433 bytes
-rw-r--r--doc/images/port_service_mode.pngbin0 -> 85093 bytes
-rw-r--r--doc/images/router_arp.pngbin0 -> 25576 bytes
-rw-r--r--doc/images/topology.pngbin0 -> 61332 bytes
-rw-r--r--doc/images/xl710_vs_mlx5_64b.pngbin0 -> 10607 bytes
-rw-r--r--doc/images/xl710_vs_mlx5_var_size.pngbin0 -> 16875 bytes
-rwxr-xr-xdoc/release_notes.asciidoc46
-rwxr-xr-xdoc/trex-analytics-howto.asciidoc58
-rw-r--r--doc/trex_analytics-docinfo.html22
-rwxr-xr-xdoc/trex_analytics.asciidoc202
-rwxr-xr-xdoc/trex_book.asciidoc653
-rw-r--r--doc/trex_faq.asciidoc54
-rw-r--r--doc/trex_index.asciidoc8
-rwxr-xr-xdoc/trex_stateless.asciidoc370
-rw-r--r--doc/trex_toc.asciidoc2
-rwxr-xr-xdoc/visio_drawings/illustrations_stateless.vsdbin5346816 -> 5927936 bytes
-rw-r--r--doc/visio_drawings/topology.vsdbin0 -> 1317888 bytes
-rwxr-xr-xdoc/waf-1.9.3170
-rwxr-xr-xdoc/waf-1.9.5169
-rwxr-xr-xdoc/ws_main.py84
-rwxr-xr-xdoc/wscript3
-rw-r--r--external_libs/ibverbs/include/infiniband/arch.h160
-rw-r--r--external_libs/ibverbs/include/infiniband/driver.h236
-rw-r--r--external_libs/ibverbs/include/infiniband/driver_exp.h151
-rw-r--r--external_libs/ibverbs/include/infiniband/kern-abi.h1144
-rw-r--r--external_libs/ibverbs/include/infiniband/kern-abi_exp.h722
-rw-r--r--external_libs/ibverbs/include/infiniband/marshall.h65
-rw-r--r--external_libs/ibverbs/include/infiniband/mlx5_hw.h796
-rw-r--r--external_libs/ibverbs/include/infiniband/ofa_verbs.h210
-rw-r--r--external_libs/ibverbs/include/infiniband/opcode.h147
-rw-r--r--external_libs/ibverbs/include/infiniband/peer_ops.h370
-rw-r--r--external_libs/ibverbs/include/infiniband/sa-kern-abi.h65
-rw-r--r--external_libs/ibverbs/include/infiniband/sa.h135
-rw-r--r--external_libs/ibverbs/include/infiniband/verbs.h1642
-rw-r--r--external_libs/ibverbs/include/infiniband/verbs_exp.h3585
-rw-r--r--external_libs/ibverbs/libibverbs.abin0 -> 937626 bytes
-rw-r--r--external_libs/ibverbs/libibverbs.la41
-rw-r--r--external_libs/ibverbs/libibverbs.lai41
-rw-r--r--external_libs/ibverbs/libibverbs.sobin0 -> 494224 bytes
-rw-r--r--external_libs/ibverbs/libibverbs.so.1bin0 -> 494224 bytes
-rw-r--r--external_libs/ibverbs/libibverbs.so.1.0.0bin0 -> 494224 bytes
-rw-r--r--external_libs/ibverbs/readme.txt30
-rwxr-xr-xlinux/b2
-rwxr-xr-xlinux/waf-1.9.3170
-rwxr-xr-xlinux/waf-1.9.5169
-rwxr-xr-xlinux/ws_main.py5
-rwxr-xr-xlinux_dpdk/b2
-rwxr-xr-xlinux_dpdk/waf-1.9.3170
-rwxr-xr-xlinux_dpdk/waf-1.9.5169
-rwxr-xr-xlinux_dpdk/ws_main.py146
-rwxr-xr-xscripts/automation/regression/CPlatform.py15
-rwxr-xr-xscripts/automation/regression/aggregate_results.py63
-rw-r--r--scripts/automation/regression/cfg/client_cfg.yaml48
-rwxr-xr-xscripts/automation/regression/functional_tests/trex_cfg_creator_test.py25
-rw-r--r--scripts/automation/regression/setups/trex03/benchmark.yaml (renamed from scripts/automation/regression/setups/trex15/benchmark.yaml)0
-rw-r--r--scripts/automation/regression/setups/trex03/config.yaml (renamed from scripts/automation/regression/setups/trex17/config.yaml)2
-rw-r--r--scripts/automation/regression/setups/trex06/benchmark.yaml (renamed from scripts/automation/regression/setups/trex17/benchmark.yaml)2
-rw-r--r--scripts/automation/regression/setups/trex06/config.yaml (renamed from scripts/automation/regression/setups/trex15/config.yaml)2
-rw-r--r--scripts/automation/regression/setups/trex07/backup/benchmark.yaml244
-rw-r--r--scripts/automation/regression/setups/trex07/backup/config.yaml66
-rw-r--r--scripts/automation/regression/setups/trex07/benchmark.yaml173
-rw-r--r--scripts/automation/regression/setups/trex07/config.yaml29
-rw-r--r--scripts/automation/regression/setups/trex08/benchmark.yaml50
-rw-r--r--scripts/automation/regression/setups/trex09/benchmark.yaml6
-rw-r--r--scripts/automation/regression/setups/trex11/backup/benchmark.yaml155
-rw-r--r--scripts/automation/regression/setups/trex11/backup/config.yaml38
-rw-r--r--scripts/automation/regression/setups/trex11/benchmark.yaml67
-rw-r--r--scripts/automation/regression/setups/trex11/config.yaml23
-rw-r--r--scripts/automation/regression/setups/trex14/BU/benchmark.yaml245
-rw-r--r--scripts/automation/regression/setups/trex14/BU/config.yaml67
-rw-r--r--scripts/automation/regression/setups/trex14/benchmark.yaml21
-rw-r--r--scripts/automation/regression/setups/trex14/config.yaml15
-rw-r--r--scripts/automation/regression/stateful_tests/trex_client_cfg_test.py52
-rwxr-xr-xscripts/automation/regression/stateful_tests/trex_general_test.py20
-rw-r--r--scripts/automation/regression/stateless_tests/stl_client_test.py20
-rw-r--r--scripts/automation/regression/stateless_tests/stl_performance_test.py4
-rw-r--r--scripts/automation/regression/stateless_tests/stl_rx_test.py23
-rw-r--r--scripts/automation/regression/trex.py2
-rwxr-xr-xscripts/automation/regression/trex_unit_test.py36
-rwxr-xr-xscripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py4
-rwxr-xr-xscripts/automation/trex_control_plane/stl/console/trex_console.py50
-rw-r--r--scripts/automation/trex_control_plane/stl/console/trex_tui.py6
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir_update.py123
-rw-r--r--scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json269
-rwxr-xr-xscripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py209
-rw-r--r--scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py3
-rw-r--r--scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py45
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py61
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py919
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py47
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py87
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py426
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py255
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py23
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py60
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py30
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py22
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py252
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py13
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py16
-rwxr-xr-xscripts/dpdk_nic_bind.py17
-rwxr-xr-xscripts/dpdk_setup_ports.py274
-rw-r--r--scripts/dumy_libs/libibverbs.so.1bin0 -> 494224 bytes
-rw-r--r--scripts/dumy_libs/libnl-3.so.200bin0 -> 110096 bytes
-rw-r--r--scripts/dumy_libs/libnl-route-3.so.200bin0 -> 293640 bytes
-rwxr-xr-xscripts/exp/dns_ipv6_rxcheck-ex.erfbin304 -> 304 bytes
-rw-r--r--scripts/exp/dns_ipv6_rxcheck.erfbin304 -> 304 bytes
-rwxr-xr-xscripts/exp/dns_rxcheck-ex.erfbin256 -> 256 bytes
-rw-r--r--scripts/exp/dns_rxcheck.erfbin256 -> 256 bytes
-rwxr-xr-xscripts/exp/ipv6-0-ex.erfbin11200 -> 11200 bytes
-rw-r--r--scripts/exp/ipv6-0.erfbin11200 -> 11200 bytes
-rwxr-xr-xscripts/exp/ipv6_vlan-0-ex.erfbin11200 -> 11200 bytes
-rw-r--r--scripts/exp/ipv6_vlan-0.erfbin11200 -> 11200 bytes
-rwxr-xr-xscripts/exp/rtsp_short1_ipv6_rxcheck-ex.erfbin21560 -> 21560 bytes
-rw-r--r--scripts/exp/rtsp_short1_ipv6_rxcheck.erfbin21560 -> 21560 bytes
-rwxr-xr-xscripts/exp/rtsp_short1_rxcheck-ex.erfbin20912 -> 20912 bytes
-rw-r--r--scripts/exp/rtsp_short1_rxcheck.erfbin20912 -> 20912 bytes
-rw-r--r--scripts/external_libs/pyzmq-14.5.0/python2/cel59/32bit/zmq/sugar/context.py20
-rw-r--r--scripts/external_libs/pyzmq-14.5.0/python2/cel59/64bit/zmq/sugar/context.py20
-rw-r--r--scripts/external_libs/pyzmq-14.5.0/python2/fedora18/64bit/zmq/sugar/context.py20
-rw-r--r--scripts/external_libs/pyzmq-14.5.0/python3/cel59/32bit/zmq/sugar/context.py20
-rw-r--r--scripts/external_libs/pyzmq-14.5.0/python3/cel59/64bit/zmq/sugar/context.py20
-rw-r--r--scripts/external_libs/pyzmq-14.5.0/python3/fedora18/64bit/zmq/sugar/context.py20
-rw-r--r--scripts/ko/3.10.0-327.el7.x86_64/igb_uio.kobin0 -> 236751 bytes
-rwxr-xr-xscripts/master_daemon.py8
-rwxr-xr-xscripts/t-rex-646
-rwxr-xr-xscripts/t-rex-64-debug-gdb7
-rwxr-xr-xscripts/t-rex-64-valgrind69
-rwxr-xr-xscripts/trex-cfg24
-rwxr-xr-xscripts/trex_show_threads.py4
-rw-r--r--scripts/valgrind.sup57
-rwxr-xr-xsrc/bp_gtest.cpp2
-rwxr-xr-xsrc/bp_sim.cpp49
-rwxr-xr-xsrc/bp_sim.h121
-rw-r--r--src/common/Network/Packet/Arp.h43
-rwxr-xr-xsrc/common/Network/Packet/EthernetHeader.h6
-rwxr-xr-xsrc/common/Network/Packet/IPHeader.h9
-rw-r--r--src/common/Network/Packet/IcmpHeader.h5
-rwxr-xr-xsrc/common/Network/Packet/MacAddress.h133
-rwxr-xr-xsrc/common/basic_utils.cpp44
-rwxr-xr-xsrc/common/basic_utils.h6
-rwxr-xr-xsrc/common/captureFile.h5
-rwxr-xr-xsrc/common/erf.cpp6
-rwxr-xr-xsrc/common/erf.h7
-rwxr-xr-xsrc/common/pcap.cpp7
-rwxr-xr-xsrc/common/pcap.h12
-rw-r--r--src/debug.cpp17
-rw-r--r--src/dpdk/drivers/net/enic/base/vnic_dev.c38
-rw-r--r--src/dpdk/drivers/net/enic/base/vnic_dev.h3
-rw-r--r--src/dpdk/drivers/net/enic/base/vnic_devcmd.h346
-rw-r--r--src/dpdk/drivers/net/enic/enic.h14
-rw-r--r--src/dpdk/drivers/net/enic/enic_clsf.c352
-rw-r--r--src/dpdk/drivers/net/enic/enic_ethdev.c9
-rw-r--r--src/dpdk/drivers/net/enic/enic_main.c6
-rw-r--r--src/dpdk/drivers/net/enic/enic_res.c5
-rw-r--r--src/dpdk/drivers/net/ixgbe/ixgbe_ethdev.c6
-rw-r--r--src/dpdk/drivers/net/mlx5/mlx5.c20
-rw-r--r--src/dpdk/drivers/net/mlx5/mlx5.h31
-rw-r--r--src/dpdk/drivers/net/mlx5/mlx5_autoconf.h8
-rw-r--r--src/dpdk/drivers/net/mlx5/mlx5_fdir.c98
-rw-r--r--src/dpdk/drivers/net/mlx5/mlx5_rxq.c4
-rw-r--r--src/dpdk/drivers/net/mlx5/mlx5_rxtx.c4
-rw-r--r--src/dpdk/drivers/net/mlx5/mlx5_rxtx.h4
-rw-r--r--src/dpdk/drivers/net/mlx5/mlx5_stats.c321
-rw-r--r--src/flow_stat.cpp25
-rw-r--r--src/flow_stat_parser.cpp4
-rw-r--r--src/gtest/client_cfg_test.cpp186
-rw-r--r--src/gtest/trex_stateless_gtest.cpp4
-rw-r--r--src/internal_api/trex_platform_api.h11
-rw-r--r--src/main_dpdk.cpp1250
-rw-r--r--src/main_dpdk.h3
-rwxr-xr-xsrc/pal/linux_dpdk/x86_64-default-linuxapp-gcc/include/rte_config.h8
-rw-r--r--src/pkt_gen.cpp18
-rw-r--r--src/pkt_gen.h3
-rw-r--r--src/pre_test.cpp641
-rw-r--r--src/pre_test.h79
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_general.cpp305
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_stream.cpp33
-rw-r--r--src/rpc-server/commands/trex_rpc_cmds.h19
-rw-r--r--src/rpc-server/trex_rpc_cmd.cpp38
-rw-r--r--src/rpc-server/trex_rpc_cmd_api.h20
-rw-r--r--src/rpc-server/trex_rpc_cmds_table.cpp6
-rw-r--r--src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp9
-rw-r--r--src/rpc-server/trex_rpc_jsonrpc_v2_parser.h9
-rw-r--r--src/rpc-server/trex_rpc_req_resp_server.cpp24
-rw-r--r--src/rpc-server/trex_rpc_req_resp_server.h1
-rwxr-xr-xsrc/rx_check.cpp4
-rw-r--r--src/stateful_rx_core.cpp172
-rw-r--r--src/stateful_rx_core.h38
-rw-r--r--src/stateless/cp/trex_exception.h5
-rw-r--r--src/stateless/cp/trex_stateless_port.cpp179
-rw-r--r--src/stateless/cp/trex_stateless_port.h84
-rw-r--r--src/stateless/dp/trex_stateless_dp_core.cpp13
-rw-r--r--src/stateless/dp/trex_stateless_dp_core.h2
-rw-r--r--src/stateless/dp/trex_stream_node.h6
-rw-r--r--src/stateless/messaging/trex_stateless_messaging.cpp85
-rw-r--r--src/stateless/messaging/trex_stateless_messaging.h220
-rw-r--r--src/stateless/rx/trex_stateless_rx_core.cpp434
-rw-r--r--src/stateless/rx/trex_stateless_rx_core.h117
-rw-r--r--src/stateless/rx/trex_stateless_rx_defs.h62
-rw-r--r--src/stateless/rx/trex_stateless_rx_port_mngr.cpp889
-rw-r--r--src/stateless/rx/trex_stateless_rx_port_mngr.h517
-rw-r--r--src/trex_client_config.cpp265
-rw-r--r--src/trex_client_config.h304
-rw-r--r--src/trex_port_attr.cpp152
-rwxr-xr-xsrc/trex_port_attr.h150
-rwxr-xr-xsrc/tuple_gen.cpp94
-rwxr-xr-xsrc/tuple_gen.h18
-rw-r--r--src/utl_ip.cpp139
-rw-r--r--src/utl_ip.h269
-rwxr-xr-xsrc/utl_term_io.cpp7
-rwxr-xr-xsrc/utl_yaml.cpp75
-rwxr-xr-xsrc/utl_yaml.h1
221 files changed, 23514 insertions, 2880 deletions
diff --git a/.gitignore b/.gitignore
index 8787f80e..0b6832f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,7 +96,7 @@ src/GTAGS
*.bak
*.pyc
__pycache__
-*_GENERATED.py
+*_GENERATED.*
# Packages #
############
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 00000000..19f5c54c
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,4 @@
+[gerrit]
+host=gerrit.fd.io
+port=29418
+project=trex
diff --git a/VERSION b/VERSION
index f026c88d..fac09c88 100755
--- a/VERSION
+++ b/VERSION
@@ -1,4 +1,4 @@
-v2.10
+v2.12
diff --git a/doc/AnalyticsConnect.py b/doc/AnalyticsConnect.py
new file mode 100755
index 00000000..bb473c52
--- /dev/null
+++ b/doc/AnalyticsConnect.py
@@ -0,0 +1,195 @@
+"""Hello Analytics Reporting API V4."""
+
+import argparse
+
+from apiclient.discovery import build
+from oauth2client.service_account import ServiceAccountCredentials
+
+import httplib2
+from oauth2client import client
+from oauth2client import file
+from oauth2client import tools
+
+from pprint import pprint
+import time
+
+SCOPES = ['https://www.googleapis.com/auth/analytics.readonly']
+DISCOVERY_URI = ('https://analyticsreporting.googleapis.com/$discovery/rest')
+KEY_FILE_LOCATION = '/auto/srg-sce-swinfra-usr/emb/users/itraviv/GoogleAnalytics/GA_ReportingAPI/My Project-da37fc42de8f.p12'
+SERVICE_ACCOUNT_EMAIL = 'trex-cisco@i-jet-145907.iam.gserviceaccount.com'
+VIEW_ID = '120207451'
+
+
+def initialize_analyticsreporting():
+ """Initializes an analyticsreporting service object.
+
+ Returns:
+ analytics an authorized analyticsreporting service object.
+ """
+
+ credentials = ServiceAccountCredentials.from_p12_keyfile(
+ SERVICE_ACCOUNT_EMAIL, KEY_FILE_LOCATION, scopes=SCOPES)
+
+ http = credentials.authorize(httplib2.Http())
+
+ # Build the service object.
+ analytics = build('analytics', 'v4', http=http, discoveryServiceUrl=DISCOVERY_URI)
+
+ return analytics
+
+
+def get_report(analytics, start_date='2016-11-27', end_date='2016-11-27'):
+ # Use the Analytics Service Object to query the Analytics Reporting API V4.
+ return analytics.reports().batchGet(
+ body={
+ 'reportRequests': [
+ {
+ 'viewId': VIEW_ID,
+ 'dateRanges': [{'startDate': start_date, 'endDate': end_date}],
+ 'metrics': [{'expression': 'ga:metric1', 'formattingType': 'CURRENCY'},
+ {'expression': 'ga:metric2', 'formattingType': 'CURRENCY'},
+ {'expression': 'ga:metric3', 'formattingType': 'CURRENCY'},
+ {'expression': 'ga:totalEvents'}],
+ 'dimensions': [{"name": "ga:eventAction"}, {"name": "ga:dimension1"}, {"name": "ga:dimension2"},
+ {"name": "ga:dimension3"},
+ {"name": "ga:date"}, {"name": "ga:hour"}, {"name": "ga:minute"}],
+ 'pageSize': 10000
+ }
+ ]
+ }
+ ).execute()
+
+
+def print_response(response):
+ """Parses and prints the Analytics Reporting API V4 response"""
+
+ for report in response.get('reports', []):
+ columnHeader = report.get('columnHeader', {})
+ dimensionHeaders = columnHeader.get('dimensions', [])
+ metricHeaders = columnHeader.get('metricHeader', {}).get('metricHeaderEntries', [])
+ rows = report.get('data', {}).get('rows', [])
+
+ for row in rows:
+ dimensions = row.get('dimensions', [])
+ dateRangeValues = row.get('metrics', [])
+
+ for header, dimension in zip(dimensionHeaders, dimensions):
+ print header + ': ' + dimension
+
+ for i, values in enumerate(dateRangeValues):
+ print 'Date range (' + str(i) + ')'
+ for metricHeader, value in zip(metricHeaders, values.get('values')):
+ print metricHeader.get('name') + ': ' + value
+
+
+def export_to_tuples(response):
+ # counter = 0
+ setups = set()
+ df = {}
+ for report in response.get('reports', []):
+ rows = report.get('data', {}).get('rows', [])
+ for row in rows:
+ data = []
+ dimensions = row.get('dimensions', [])
+ # print 'this is dimensions'
+ # print dimensions
+ data.append(dimensions[1]) # test name
+ data.append(dimensions[2]) # state
+ # data.append(dimensions[3]) # setup
+ data.append(dimensions[4]) # date in YYYYMMDD format
+ data.append(dimensions[5]) # hour
+ data.append(dimensions[6]) # minute
+ dateRangeValues = row.get('metrics', [])
+ value = dateRangeValues[0].get('values', [])[0] # MPPS
+ golden_min = dateRangeValues[0].get('values', [])[1] # golden min
+ golden_max = dateRangeValues[0].get('values', [])[2] # golden max
+ data.append(value)
+ # counter += 1
+ data.append(golden_min)
+ data.append(golden_max)
+ data.append(dimensions[0]) # build id
+ if dimensions[3] in setups:
+ if dimensions[1] in df[dimensions[3]]:
+ df[dimensions[3]][dimensions[1]].append(tuple(data))
+ else:
+ df[dimensions[3]][dimensions[1]] = [tuple(data)]
+ else:
+ df[dimensions[3]] = {}
+ df[dimensions[3]][dimensions[1]] = [tuple(data)]
+ setups.add(dimensions[3])
+ # print 'counter is: %d' % counter
+ return df, setups
+
+
+def main():
+ analytics = initialize_analyticsreporting()
+ response = get_report(analytics)
+ df, setups = export_to_tuples(response)
+ # pprint(df)
+ return df, setups
+
+
+if __name__ == '__main__':
+ main()
+
+"""
+response structure (when fetched with "export to tuples"):
+
+{ 'setup1': {'test_name1': [(test_res1),(test_res2),...],
+ 'test_name2': [(test_res1),(test_res2),...]
+ },
+ 'setup2': {'test_name1': [(test_res1),(test_res2),...],
+ 'test_name2': [(test_res1),(test_res2),...]
+ },
+ .
+ .
+ .
+ .
+}
+
+{u'kiwi02': {u'VM - 64 bytes, multi CPU, cache size 1024': [(u'VM - 64 bytes, multi CPU, cache size 1024',
+ u'stl',
+ u'performance',
+ u'19.711146',
+ u'19.0',
+ u'22.0'),
+ (u'VM - 64 bytes, multi CPU, cache size 1024',
+ u'stl',
+ u'performance',
+ u'19.581567',
+ u'19.0',
+ u'22.0')],
+ u'VM - 64 bytes, multi CPUs': [(u'VM - 64 bytes, multi CPUs',
+ u'stl',
+ u'performance',
+ u'10.398847',
+ u'9.7',
+ u'12.5'),
+ (u'VM - 64 bytes, multi CPUs',
+ u'stl',
+ u'performance',
+ u'10.925308',
+ u'9.7',
+ u'12.5')
+ ]
+ }
+ u'trex07': {u'VM - 64 bytes, multi CPU, cache size 1024': [(u'VM - 64 bytes, multi CPU, cache size 1024',
+ u'stl',
+ u'performance',
+ u'25.078212',
+ u'9.0',
+ u'15.0')
+ ]
+ u'VM - 64 bytes, multi CPUs': [(u'VM - 64 bytes, multi CPUs',
+ u'stl',
+ u'performance',
+ u'9.469138',
+ u'8.5',
+ u'12.0')
+ ]
+ }
+}
+
+
+
+"""
diff --git a/doc/AnalyticsWebReport.py b/doc/AnalyticsWebReport.py
new file mode 100755
index 00000000..1806cab9
--- /dev/null
+++ b/doc/AnalyticsWebReport.py
@@ -0,0 +1,30 @@
+import os
+import sys
+import AnalyticsConnect as ac
+import TRexDataAnalysis as tr
+import time
+import datetime
+
+
+def main(verbose=False, detailed_test_stats=''):
+ if verbose:
+ print('Retrieving data from Google Analytics')
+ analytics = ac.initialize_analyticsreporting()
+ current_date = time.strftime("%Y-%m-%d")
+ k_days_ago = datetime.datetime.now() - datetime.timedelta(days=15)
+ start_date = str(k_days_ago.date())
+ response = ac.get_report(analytics, start_date, current_date)
+ ga_all_data_dict, setups = ac.export_to_tuples(response)
+ dest_path = os.path.join(os.getcwd(), 'build', 'images')
+ if verbose:
+ print('Saving data to %s' % dest_path)
+ if detailed_test_stats:
+ print('generating detailed table for test results')
+ tr.create_all_data(ga_all_data_dict, setups, start_date, current_date, save_path=dest_path,
+ add_stats='yes', detailed_test_stats=detailed_test_stats)
+ if verbose:
+ print('Done without errors.')
+
+
+if __name__ == "__main__":
+ main()
diff --git a/doc/README b/doc/README
new file mode 100644
index 00000000..8c630ff0
--- /dev/null
+++ b/doc/README
@@ -0,0 +1,17 @@
+How to build the docs:
+
+You will need following packages installed:
+
+ * asciidoc
+ * dblatex
+ * source-highlight
+ * python-sphinx
+
+(apt install ...) for Ubuntu
+(yum install ...) for Fedora/CentOS
+
+Then,
+
+./b configure
+./b build
+
diff --git a/doc/TRexDataAnalysis.py b/doc/TRexDataAnalysis.py
new file mode 100755
index 00000000..4cd928bb
--- /dev/null
+++ b/doc/TRexDataAnalysis.py
@@ -0,0 +1,171 @@
+#!/scratch/Anaconda2.4.0/bin/python
+import pandas as pd
+import numpy as np
+import matplotlib
+
+matplotlib.use('Agg')
+from matplotlib import pyplot as plt
+import os
+import time
+
+
+def generate_dframe_for_test(setup_name, test_name, test_data):
+ test_results = []
+ test_dates = []
+ test_build_ids = []
+ test_mins = set()
+ test_maxs = set()
+ for query in test_data:
+ test_results.append(float(query[5]))
+ date_formatted = time.strftime("%d-%m-%Y", time.strptime(query[2], "%Y%m%d"))
+ time_of_res = date_formatted + '-' + query[3] + ':' + query[4]
+ test_dates.append(time_of_res)
+ test_build_ids.append(query[8])
+ test_mins.add(float(query[6]))
+ test_maxs.add(float(query[7]))
+ df = pd.DataFrame({test_name: test_results})
+ df_detailed = pd.DataFrame({(test_name + ' Results'): test_results, (test_name + ' Date'): test_dates,
+ "Setup": ([setup_name] * len(test_results)), "Build Id": test_build_ids})
+ stats = tuple([float(df.mean()), min(test_mins), max(test_maxs)]) # stats = (avg_mpps,min,max)
+ return df, stats, df_detailed
+
+
+def generate_dframe_arr_and_stats_of_tests_per_setup(date, setup_name, setup_dict):
+ dframe_arr_trend = []
+ stats_arr = []
+ dframe_arr_latest = []
+ dframe_arr_detailed = []
+ test_names = setup_dict.keys()
+ for test in test_names:
+ df, stats, df_detailed = generate_dframe_for_test(setup_name, test, setup_dict[test])
+ dframe_arr_detailed.append(df_detailed)
+ dframe_arr_trend.append(df)
+ stats_arr.append(stats)
+ df_latest = float(setup_dict[test][-1][5])
+ dframe_arr_latest.append(df_latest)
+ dframe_arr_latest = pd.DataFrame({'Date': [date] * len(dframe_arr_latest),
+ 'Setup': [setup_name],
+ 'Test Name': test_names,
+ 'MPPS': dframe_arr_latest},
+ index=range(1, len(dframe_arr_latest) + 1))
+ stats_df = pd.DataFrame(stats_arr, index=setup_dict.keys(), columns=['Avg MPPS', 'Golden Min', 'Golden Max'])
+ stats_df.index.name = 'Test Name'
+ return dframe_arr_trend, stats_df, dframe_arr_latest, dframe_arr_detailed
+
+
+def create_plot_for_dframe_arr(dframe_arr, setup_name, start_date, end_date, show='no', save_path='',
+ file_name='_trend_graph'):
+ dframe_all = pd.concat(dframe_arr, axis=1)
+ dframe_all = dframe_all.astype(float)
+ dframe_all.plot()
+ plt.legend(fontsize='small', loc='best')
+ plt.ylabel('MPPS')
+ plt.title('Setup: ' + setup_name)
+ plt.tick_params(
+ axis='x',
+ which='both',
+ bottom='off',
+ top='off',
+ labelbottom='off')
+ plt.xlabel('Time Period: ' + start_date + ' - ' + end_date)
+ if save_path:
+ plt.savefig(os.path.join(save_path, setup_name + file_name + '.png'))
+ if show == 'yes':
+ plt.show()
+
+
+def create_bar_plot_for_latest_runs_per_setup(dframe_all_tests_latest, setup_name, show='no', save_path=''):
+ plt.figure()
+ colors_for_bars = ['b', 'g', 'r', 'c', 'm', 'y']
+ dframe_all_tests_latest['MPPS'].plot(kind='bar', legend=False, color = colors_for_bars)
+ dframe_all_tests_latest = dframe_all_tests_latest[['Test Name', 'Setup', 'Date', 'MPPS']]
+ plt.xticks(rotation='horizontal')
+ plt.xlabel('Index of Tests')
+ plt.ylabel('MPPS')
+ plt.title("Test Runs for Setup: " + setup_name)
+ if save_path:
+ plt.savefig(os.path.join(save_path, setup_name + '_latest_test_runs.png'))
+ dframe_all_tests_latest = dframe_all_tests_latest.round(2)
+ dframe_all_tests_latest.to_csv(os.path.join(save_path, setup_name + '_latest_test_runs_stats.csv'))
+ if show == 'yes':
+ plt.show()
+
+
+def create_all_data_per_setup(setup_dict, setup_name, start_date, end_date, show='no', save_path='', add_stats='',
+ detailed_test_stats=''):
+ dframe_arr, stats_arr, dframe_latest_arr, dframe_detailed = generate_dframe_arr_and_stats_of_tests_per_setup(
+ end_date, setup_name,
+ setup_dict)
+ if detailed_test_stats:
+ detailed_table = create_detailed_table(dframe_detailed)
+ else:
+ detailed_table = []
+ create_bar_plot_for_latest_runs_per_setup(dframe_latest_arr, setup_name, show=show, save_path=save_path)
+ create_plot_for_dframe_arr(dframe_arr, setup_name, start_date, end_date, show, save_path)
+ if add_stats:
+ stats_arr = stats_arr.round(2)
+ stats_arr.to_csv(os.path.join(save_path, setup_name + '_trend_stats.csv'))
+ plt.close('all')
+ return detailed_table, dframe_latest_arr
+
+
+def create_detailed_table(dframe_arr_detailed):
+ result = reduce(lambda x, y: pd.merge(x, y, on=('Build Id', 'Setup')), dframe_arr_detailed)
+ return result
+
+
+def latest_runs_comparison_bar_chart(setup_name1, setup_name2, setup1_latest_result, setup2_latest_result, save_path='',
+ show='no'):
+ s1_res = setup1_latest_result[['Test Name', 'MPPS']]
+ s2_res = setup2_latest_result[['Test Name', 'MPPS','Date']]
+ s1_res.columns = ['Test Name', setup_name1]
+ s2_res.columns = ['Test Name', setup_name2, 'Date']
+ compare_dframe = pd.merge(s1_res, s2_res, on='Test Name')
+ compare_dframe.plot(kind='bar')
+ plt.legend(fontsize='small', loc='best')
+ plt.xticks(rotation='horizontal')
+ plt.xlabel('Index of Tests')
+ plt.ylabel('MPPS')
+ plt.title("Comparison between " + setup_name1 + " and " + setup_name2)
+ if save_path:
+ plt.savefig(os.path.join(save_path, "_comparison.png"))
+ compare_dframe = compare_dframe.round(2)
+ compare_dframe.to_csv(os.path.join(save_path, '_comparison_stats_table.csv'))
+ if show == 'yes':
+ plt.show()
+
+
+# WARNING: if the file _all_stats.csv already exists, this script deletes it, to prevent overflowing of data
+# since data is appended to the file
+def create_all_data(ga_data, setup_names, start_date, end_date, save_path='', add_stats='', detailed_test_stats=''):
+ total_detailed_data = []
+ trex07_latest = []
+ trex08_latest = []
+ if detailed_test_stats:
+ if os.path.exists(os.path.join(save_path, '_detailed_table.csv')):
+ os.remove(os.path.join(save_path, '_detailed_table.csv'))
+ for setup_name in setup_names:
+ if setup_name == 'trex07':
+ detailed_setup_data, trex07_latest = create_all_data_per_setup(ga_data[setup_name], setup_name, start_date,
+ end_date,
+ show='no', save_path=save_path,
+ add_stats=add_stats,
+ detailed_test_stats=detailed_test_stats)
+ elif setup_name == 'trex08':
+ detailed_setup_data, trex08_latest = create_all_data_per_setup(ga_data[setup_name], setup_name, start_date,
+ end_date,
+ show='no', save_path=save_path,
+ add_stats=add_stats,
+ detailed_test_stats=detailed_test_stats)
+ else:
+ detailed_setup_data = create_all_data_per_setup(ga_data[setup_name], setup_name, start_date, end_date,
+ show='no', save_path=save_path,
+ add_stats=add_stats,
+ detailed_test_stats=detailed_test_stats)[0]
+ total_detailed_data.append(detailed_setup_data)
+ if detailed_test_stats:
+ total_detailed_dframe = pd.DataFrame().append(total_detailed_data)
+ total_detailed_dframe.to_csv(os.path.join(save_path, '_detailed_table.csv'))
+ latest_runs_comparison_bar_chart('Mellanox ConnectX-4',
+ 'Intel XL710', trex07_latest, trex08_latest,
+ save_path=save_path, show='no')
diff --git a/doc/b b/doc/b
index 58e6bddf..c48cb4e9 100755
--- a/doc/b
+++ b/doc/b
@@ -1,6 +1,6 @@
#! /bin/bash
-waf=waf-1.9.3
+waf=waf-1.9.5
p2=${PYTHON:-${PYTHON2:-python2.7}}
if which $p2 &> /dev/null; then
diff --git a/doc/images/port_normal_mode.png b/doc/images/port_normal_mode.png
new file mode 100644
index 00000000..3ea01d7f
--- /dev/null
+++ b/doc/images/port_normal_mode.png
Binary files differ
diff --git a/doc/images/port_service_mode.png b/doc/images/port_service_mode.png
new file mode 100644
index 00000000..a689e34d
--- /dev/null
+++ b/doc/images/port_service_mode.png
Binary files differ
diff --git a/doc/images/router_arp.png b/doc/images/router_arp.png
new file mode 100644
index 00000000..ad642c16
--- /dev/null
+++ b/doc/images/router_arp.png
Binary files differ
diff --git a/doc/images/topology.png b/doc/images/topology.png
new file mode 100644
index 00000000..4e2ee65b
--- /dev/null
+++ b/doc/images/topology.png
Binary files differ
diff --git a/doc/images/xl710_vs_mlx5_64b.png b/doc/images/xl710_vs_mlx5_64b.png
new file mode 100644
index 00000000..97f07d47
--- /dev/null
+++ b/doc/images/xl710_vs_mlx5_64b.png
Binary files differ
diff --git a/doc/images/xl710_vs_mlx5_var_size.png b/doc/images/xl710_vs_mlx5_var_size.png
new file mode 100644
index 00000000..fb9614f2
--- /dev/null
+++ b/doc/images/xl710_vs_mlx5_var_size.png
Binary files differ
diff --git a/doc/release_notes.asciidoc b/doc/release_notes.asciidoc
index b37e467e..95975830 100755
--- a/doc/release_notes.asciidoc
+++ b/doc/release_notes.asciidoc
@@ -23,6 +23,52 @@ ifdef::backend-docbook[]
endif::backend-docbook[]
+== Release 2.12 ==
+
+* Improve support for Mellanox ConnectX-4 cards (100/50/25GbE)
+** Only CentOs/RedHat 7.2 and up is supported due to OFED issues
+** Improve 64Byte Stateleess performance
+* Cisco VIC is fully supported
+* Stateless L2/L3 configuration mode for ports - link:trex_stateless.html#_port_layer_mode_configuration[Port Layer Mode Configuration]
+* Stateless neighboring protocols infra and first protocols support/Python API - link:trex_stateless.html#_neighboring_protocols[Neighboring Protocols]
+* Stateful - ARP support for Client clustering mode (a.k.a APIC-EM) see here link:trex_manual.html#_client_clustering_configuration[Client Clustering]
+* Added Stateless performance trend document link:trex_analytics.html[trex performance trend]
+
+
+[IMPORTANT]
+=====================================
+Change of behavior - trex config file (/trex/cfg.yaml) should include a distinct MAC address or distinct IP address.
+Not setting the port information :
+ before v2.12 : src MAC addr == 00:00:00:01:00:00
+ v2.12 and up : src MAC addr == HW MAC address
+===========================
+
+
+=== fix issues: ===
+
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-257[trex-257]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-265[trex-265]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-280[trex-280]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-277[trex-277]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-278[trex-278]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-199[trex-199]
+
+
+== Release 2.11 ==
+
+* Early support for Mellanox ConnectX-4 cards (100/50/25GbE) - based on contributions from Mellanox DPDK team see here for more info link:trex_manual.html#_mellanox_connectx_4_support[Mellanox ConnectX-4 Support]
+* Add support for Cisco VIC 1300 series adapter (40GbE)- based on Cisco VIC DPDK team see link:trex_manual.html#_cisco_vic_support[Cisco VIC Support]
+
+=== fix issues: ===
+
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-268[trex-268]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-267[trex-267]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-195[trex-195]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-271[trex-271]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-255[trex-255]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-249[trex-249]
+
+
== Release 2.10 ==
* Added support for IP based configuration files (As opposed to MAC based used until now), with the ability of TRex to send
diff --git a/doc/trex-analytics-howto.asciidoc b/doc/trex-analytics-howto.asciidoc
new file mode 100755
index 00000000..6ad6c216
--- /dev/null
+++ b/doc/trex-analytics-howto.asciidoc
@@ -0,0 +1,58 @@
+TRex Analytics How-To Guide
+===========================
+:email: trex.tgen@gmail.com
+:quotes.++:
+:numbered:
+:web_server_url: https://trex-tgn.cisco.com/trex
+:local_web_server_url: csi-wiki-01:8181/trex
+:toclevels: 6
+:tabledef-default.subs: normal,callouts
+include::trex_ga.asciidoc[]
+// PDF version - image width variable
+ifdef::backend-docbook[]
+:p_width: 450
+endif::backend-docbook[]
+= Requirements
+Google Analytics Integration: Google API Python client library: link:https://developers.google.com/analytics/devguides/reporting/core/v4/quickstart/installed-py[here] +
+Data Analysis: using NumPy, MatPlotLib and Pandas from Anaconda 4.2.0 link:https://www.continuum.io/downloads[here]
+
+= Setup fetch-analysis-publishing routine
+== Build an Analytic Environment
+1. Create a Google Analytics account and property using this link:https://support.google.com/analytics/answer/1008015?hl=en[link]
+2. Using the Google Analytics tracking guide, send test results to your property. link:https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide[here]
+3. Set up your account to properly fetch the data from Google Analytics, using this guide: link:https://developers.google.com/analytics/devguides/reporting/core/v4/quickstart/installed-py[here]
+
+== Fetch and organize
+1. Fetch the data into dictionary of this structure: +
+[source,python]
+----
+{ 'setup1': {'test_name1': [(test_res1),(test_res2),...],
+ 'test_name2': [(test_res1),(test_res2),...]
+ },
+ 'setup2': {'test_name1': [(test_res1),(test_res2),...],
+ 'test_name2': [(test_res1),(test_res2),...]
+ },
+ .
+ .
+ .
+ .
+}
+----
+test_res should maintain this structure:
+[source,python]
+----
+(test_name,state,test_type,MPPS,goldenMin,goldenMax)
+
+Example:
+('VM - 64 bytes, multi CPU, cache size 1024','stl','performance','19.711146','19.0','22.0')
+----
+== Analize and generate plots and data tables
+Use the script TRexDataAnalysis.py to create the plots and data tables for your test results: +
+1. run create_all_data with the entire data dictionary that was fetched, to create plots and tables for each setup provided in the dictionary. +
+provide a "save_path" to save the graphs and plots to your desired location +
+
+
+== Build the Document
+
+
+
diff --git a/doc/trex_analytics-docinfo.html b/doc/trex_analytics-docinfo.html
new file mode 100644
index 00000000..c050287b
--- /dev/null
+++ b/doc/trex_analytics-docinfo.html
@@ -0,0 +1,22 @@
+
+<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
+
+<script src="my_chart.js"></script>
+
+<style>
+.axis path,
+.axis line {
+ fill: none;
+ stroke: #000;
+ shape-rendering: crispEdges;
+}
+
+.dot {
+ stroke: #000;
+}
+</style>
+
+
+
+
+
diff --git a/doc/trex_analytics.asciidoc b/doc/trex_analytics.asciidoc
new file mode 100755
index 00000000..6635a068
--- /dev/null
+++ b/doc/trex_analytics.asciidoc
@@ -0,0 +1,202 @@
+TRex Performance Trend
+======================
+:email: trex.tgen@gmail.com
+:quotes.++:
+:numbered:
+:web_server_url: https://trex-tgn.cisco.com/trex
+:local_web_server_url: csi-wiki-01:8181/trex
+:toclevels: 6
+:tabledef-default.subs: normal,callouts
+
+include::trex_ga.asciidoc[]
+
+// PDF version - image width variable
+ifdef::backend-docbook[]
+:p_width: 450
+endif::backend-docbook[]
+
+// HTML version - image width variable
+ifdef::backend-xhtml11[]
+:p_width: 800
+endif::backend-xhtml11[]
+
+
+= TRex performance trend
+
+== Setup: XL710 (4x40GbE) trex-08
+
+.Setup Details
+[options='header',cols="1,5"]
+|=================
+| Server | Cisco UCS 240M3
+| CPU: | 2 sockets x Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz, 8 cores
+| NICs: | 2 NICS x ( 2x 40GbE ) Intel XL710, Total ports 4
+| NUMA | NIC0-NUMA 0 , NIC1-NUMA1
+| Memory | 2x16GB
+| PCIe | 2x PCIe16 + 2xPCIe4
+| OS: | Fedora 18 - baremetal
+|=================
+
+image:images/trex08_latest_test_runs.png[title="trex08 test runs",align="left",width={p_width}, link="images/trex08_latest_test_runs.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/trex08_latest_test_runs_stats.csv[]
+|===
+
+=== Trend: Performance Over Time
+
+image:images/trex08_trend_graph.png[title="trex08_trend_graph",align="left",width={p_width}, link="images/trex08_trend_graph.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/trex08_trend_stats.csv[]
+|===
+
+== Setup: X710 (8x10GbE) trex-09
+
+.Setup Details
+[options='header',cols="1,5"]
+|=================
+| Server | Cisco UCS 240M3
+| CPU: | 2 sockets x Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz, 8 cores
+| NICs: | 2 NICS x ( 2x 40GbE ) Intel X710, Total 8 ports
+| NUMA | NIC0-NUMA0 , NIC1-NUMA1
+| Memory | 2x16GB
+| PCIe | 2x PCIe16 + 2xPCIe4
+| OS: | Fedora 18 - baremetal
+|=================
+
+
+image:images/trex09_latest_test_runs.png[title="trex09 test runs",align="left",width={p_width}, link="images/trex09_latest_test_runs.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/trex09_latest_test_runs_stats.csv[]
+|===
+
+=== Trend: Performance Over Time
+
+image:images/trex09_trend_graph.png[title="trex09_trend_graph",align="left",width={p_width}, link="images/trex09_trend_graph.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/trex09_trend_stats.csv[]
+|===
+
+
+
+== Setup: 82599EB (4x10GbE) kiwi02
+
+.Setup Details
+[options='header',cols="1,5"]
+|=================
+| Server | Cisco UCS 220M3
+| CPU: | 2 x Intel(R) Xeon(R) CPU E5-2650 0 @ 2.00GHz (low end)
+| NICs: | 2(NICS)x (2x10GbE) (4 ports) Intel 82599EB Total 4 ports
+| NUMA | NIC0-NUMA0 , NIC1-NUMA1
+| Memory | 2x16GB
+| PCIe | 1x PCIe16 + 1xPCIe8
+| OS: | Fedora 18 - baremetal
+|=================
+
+
+image:images/kiwi02_latest_test_runs.png[title="trex09 test runs",align="left",width={p_width}, link="images/kiwi02_latest_test_runs.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/kiwi02_latest_test_runs_stats.csv[]
+|===
+
+=== Trend: Trend: Performance Over Time
+
+image:images/kiwi02_trend_graph.png[title="kiwi02_trend_graph",align="left",width={p_width}, link="images/kiwi02_trend_graph.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/kiwi02_trend_stats.csv[]
+|===
+
+
+
+== Setup: Cisco 1300 VIC (2x40GbE) trex-11
+
+[IMPORTANT]
+=====================================
+* this setup is not baremetal, performance are low due to that
+=====================================
+
+
+.Setup Details
+[options='header',cols="1,5"]
+|=================
+| Server | Cisco UCS 240M3
+| CPU: | 2 sockets x Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz, 8 cores
+| NICs: | 2 NICS x ( 2x 40GbE ) Intel X710, Total 8 ports
+| NUMA | Unknown due to ESXi
+| Memory | 2x16GB
+| PCIe | 2x PCIe16 + 2xPCIe4
+| OS: | Ubuntu 14 - ESXi only 4 cores are utilized
+|=================
+
+image:images/trex11_latest_test_runs.png[title="trex11 test runs",align="left",width={p_width}, link="images/trex11_latest_test_runs.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/trex11_latest_test_runs_stats.csv[]
+|===
+
+=== Trend: Performance Over Time
+
+image:images/trex11_trend_graph.png[title="trex11_trend_graph",align="left",width={p_width}, link="images/trex11_trend_graph.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/trex11_trend_stats.csv[]
+|===
+
+
+== Setup: Mellanox ConnectX-4 (2x100GbE) trex-07
+
+.Setup Details
+[options='header',cols="1,5"]
+|=================
+| Server | Cisco UCS 240
+| CPU: | 2 sockets x Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz, 8 cores
+| NICs: | 1 NIC x 100G (2 ports) Mellanox ConnectX-4 NUMA#1
+| NUMA | One NIC on NUMA1
+| Memory | 2x16GB
+| PCIe | 2x PCIe16 + 2xPCIe4
+| OS: | CentOS 7.2 - baremetal
+|=================
+
+image:images/trex07_latest_test_runs.png[title="trex07 test runs",align="left",width={p_width}, link="images/trex07_latest_test_runs.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/trex07_latest_test_runs_stats.csv[]
+|===
+
+=== Trend: Performance Over Time
+
+image:images/trex07_trend_graph.png[title="trex07_trend_graph",align="left",width={p_width}, link="images/trex07_trend_graph.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/trex07_trend_stats.csv[]
+|===
+
+== Comparison between Mellanox ConnectX-4 (2x100GbE) (trex-07) and XL710 (4x40GbE) (trex-08)
+image:images/_comparison.png[title="comparison",align="left",width={p_width}, link="images/_comparison.png"]
+
+[format="csv", options="header",halign='center']
+|===
+include::build/images/_comparison_stats_table.csv[]
+|===
+
+
+== Raw information
+
+raw information can be found here link:images/_detailed_table.csv[raw_info.csv]
+
+
diff --git a/doc/trex_book.asciidoc b/doc/trex_book.asciidoc
index 2d35787a..ffb3e474 100755
--- a/doc/trex_book.asciidoc
+++ b/doc/trex_book.asciidoc
@@ -92,14 +92,14 @@ TRex curretly works on x86 architecture and can operate well on Cisco UCS hardwa
[options="header",cols="1,3"]
|=================
| UCS Type | Comments
-| UCS C220 M3/M4 | *Preferred Low-End*. Supports up to 40Gb/sec with 540-D2. With newer Intel NIC (recommended), supports 80Gb/sec with 1RU. See table below describing components.
+| UCS C220 Mx | *Preferred Low-End*. Supports up to 40Gb/sec with 540-D2. With newer Intel NIC (recommended), supports 80Gb/sec with 1RU. See table below describing components.
| UCS C200| Early UCS model.
-| UCS C210 M2 | Supports up to 40Gb/sec PCIe3.0.
-| UCS C240 M3/M4 | *Preferred, High-End* Supports up to 200Gb/sec. 6x XL710 NICS (PCIex8) or 2xFM10K (PCIex16). See table below describing components.
+| UCS C210 Mx | Supports up to 40Gb/sec PCIe3.0.
+| UCS C240 Mx | *Preferred, High-End* Supports up to 200Gb/sec. 6x XL710 NICS (PCIex8) or 2xFM10K (PCIex16). See table below describing components.
| UCS C260M2 | Supports up to 30Gb/sec (limited by V2 PCIe).
|=================
-.Low-End UCS C220 M4 - Internal components
+.Low-End UCS C220 Mx - Internal components
[options="header",cols="1,2",width="60%"]
|=================
| Components | Details
@@ -109,7 +109,7 @@ TRex curretly works on x86 architecture and can operate well on Cisco UCS hardwa
| RAID | No RAID.
|=================
-.High-End C240 M4 - Internal components
+.High-End C240 Mx - Internal components
[options="header",cols="1,2",width="60%"]
|=================
| Components | Details
@@ -122,14 +122,17 @@ TRex curretly works on x86 architecture and can operate well on Cisco UCS hardwa
|=================
.Supported NICs
-[options="header",cols="1,1,2",width="50%"]
+[options="header",cols="1,1,4",width="90%"]
|=================
-| Bandwidth | Chipset | Example
-| 1Gb/sec | Intel I350 | Intel 4x1GE 350-T4 NIC
-| 10Gb/sec | Intel 82599| Cisco part ID:N2XX-AIPCI01 Intel x520-D2, Intel X520 Dual Port 10Gb SFP+ Adapter
-| 10Gb/sec | Intel X710 | Cisco part ID:UCSC-PCIE-IQ10GF link:https://en.wikipedia.org/wiki/Small_form-factor_pluggable_transceiver[SFP+], *Preferred* support per stream stats in hardware link:http://www.silicom-usa.com/PE310G4i71L_Quad_Port_Fiber_SFP+_10_Gigabit_Ethernet_PCI_Express_Server_Adapter_49[Silicom PE310G4i71L]
-| 40Gb/sec | Intel XL710 | Cisco part ID:UCSC-PCIE-ID40GF, link:https://en.wikipedia.org/wiki/QSFP[QSFP+] (copper/optical)
-| 100Gb/sec | Intel Intel FM10420 | QSFP28, by Silicom link:http://www.silicom-usa.com/100_Gigabit_Dual_Port_Fiber_Ethernet_PCI_Express_PE3100G2DQiR_96[Silicom PE3100G2DQiR_96] (*in development*)
+| Chipset | Bandwidth (Gb/sec) | Example
+| Intel I350 | 1 | Intel 4x1GE 350-T4 NIC
+| Intel 82599 | 10 | Cisco part ID:N2XX-AIPCI01 Intel x520-D2, Intel X520 Dual Port 10Gb SFP+ Adapter
+| Intel X710 | 10 | Cisco part ID:UCSC-PCIE-IQ10GF link:https://en.wikipedia.org/wiki/Small_form-factor_pluggable_transceiver[SFP+], *Preferred* support per stream stats in hardware link:http://www.silicom-usa.com/PE310G4i71L_Quad_Port_Fiber_SFP+_10_Gigabit_Ethernet_PCI_Express_Server_Adapter_49[Silicom PE310G4i71L]
+| Intel XL710 | 40 | Cisco part ID:UCSC-PCIE-ID40GF, link:https://en.wikipedia.org/wiki/QSFP[QSFP+] (copper/optical)
+| Intel FM10420 | 25/100 | QSFP28, by Silicom link:http://www.silicom-usa.com/100_Gigabit_Dual_Port_Fiber_Ethernet_PCI_Express_PE3100G2DQiR_96[Silicom PE3100G2DQiR_96] (*in development*)
+| Mellanox ConnectX-4 | 25/40/50/56/100 | QSFP28, link:http://www.mellanox.com/page/products_dyn?product_family=201&[ConnectX-4] link:http://www.mellanox.com/related-docs/prod_adapter_cards/PB_ConnectX-4_VPI_Card.pdf[ConnectX-4-brief] (copper/optical) supported from v2.11 more details xref:connectx_support[TRex Support]
+| Mellanox ConnectX-5 | 25/40/50/56/100 | Not supported yet
+| Cisco 1300 series | 40 | QSFP+, VIC 1380, VIC 1385, VIC 1387 see more xref:ciscovic_support[TRex Support]
| VMXNET / +
VMXNET3 (see notes) | VMware paravirtualized | Connect using VMware vSwitch
| E1000 | paravirtualized | VMware/KVM/VirtualBox
@@ -139,7 +142,7 @@ VMXNET3 (see notes) | VMware paravirtualized | Connect using VMware vSwitch
// in table above, is it correct to list "paravirtualized" as chipset? Also, what is QSFP28? It does not appear on the lined URL. Clarify: is Intel X710 the preferred NIC?
.SFP+ support
-[options="header",cols="2,1,1,1",width="70%"]
+[options="header",cols="2,1,1,1",width="90%"]
|=================
| link:https://en.wikipedia.org/wiki/Small_form-factor_pluggable_transceiver[SFP+] | Intel Ethernet Converged X710-DAX | Silicom link:http://www.silicom-usa.com/PE310G4i71L_Quad_Port_Fiber_SFP+_10_Gigabit_Ethernet_PCI_Express_Server_Adapter_49[PE310G4i71L] (Open optic) | 82599EB 10-Gigabit
| link:http://www.cisco.com/c/en/us/products/collateral/interfaces-modules/transceiver-modules/data_sheet_c78-455693.html[Cisco SFP-10G-SR] | Does not work | [green]*works* | [green]*works*
@@ -172,8 +175,31 @@ VMXNET3 (see notes) | VMware paravirtualized | Connect using VMware vSwitch
For Intel XL710 NICs, Cisco SR4/LR QSFP+ does not operate. Use Silicom with Open Optic.
=====================================
-// clarify above table and note. let's discuss.
+.ConnectX-4 NIC base QSFP28 support (100gb)
+[options="header",cols="1,2",width="90%"]
+|=================
+| link:https://en.wikipedia.org/wiki/QSFP[QSFP28] | ConnectX-4
+| QSFP28 SR4 optics | N/A
+| QSFP28 LR-4 Optics | N/A
+| QSFP28 (AoC) | Cisco QSFP-100G-AOCxM [green]*works*
+| QSFP28 DA twin-ax cables | Cisco QSFP-100G-CUxM [green]*works*
+|=================
+
+.Cisco VIC NIC base QSFP+ support
+[options="header",cols="1,2",width="90%"]
+|=================
+| link:https://en.wikipedia.org/wiki/QSFP[QSFP+] | Intel Ethernet Converged XL710-QDAX
+| QSFP+ SR4 optics | N/A
+| QSFP+ LR-4 Optics | N/A
+| QSFP Active Optical Cables (AoC) | Cisco QSFP-H40G-AOC [green]*works*
+| QSFP+ Intel Ethernet Modular Optics | N/A
+| QSFP+ DA twin-ax cables | N/A | N/A
+| Active QSFP+ Copper Cables | N/A
+|=================
+
+
+// clarify above table and note. let's discuss.
.FM10K QSFP28 support
[options="header",cols="1,1",width="70%"]
|=================
@@ -231,7 +257,8 @@ Supported Linux versions:
* Fedora 20-23, 64-bit kernel (not 32-bit)
* Ubuntu 14.04.1 LTS, 64-bit kernel (not 32-bit)
-* Ubuntu 16.xx LTS, 64-bit kernel (not 32-bit)
+* Ubuntu 16.xx LTS, 64-bit kernel (not 32-bit) -- not fully supported
+* CentOs/RedHat 7.2 LTS, 64-bit kernel (not 32-bit) -- The only working option for ConnectX-4
NOTE: Additional OS version may be supported by compiling the necessary drivers.
@@ -843,29 +870,28 @@ asr1k(config)#ipv6 route 5000::/64 3001::2
=== Client clustering configuration
-TRex supports testing complex topologies, using a feature called "client clustering".
-This feature allows more detailed clustering of clients.
+TRex supports testing complex topologies, with more than one DUT, using a feature called "client clustering".
+This feature allows specifying the distribution of clients TRex emulates.
Let's look at the following topology:
-image:images/client_clustering_topology.png[title="Client Clustering"]
-
-
-We would like to configure two clusters and direct traffic to them.
+.Topology Example
+image:images/topology.png[title="Client Clustering",width=850]
-Using config file, you can instruct TRex to generate clients
-with specific configuration per cluster.
+We have two clusters of DUTs.
+Using config file, you can partition TRex emulated clients to groups, and define
+how they will be spread between the DUT clusters.
-Cluster configuration includes:
+Group configuration includes:
* IP start range.
* IP end range.
-* Initiator side configuration.
-* Responder side configuration.
+* Initiator side configuration. - These are the parameters affecting packets sent from client side.
+* Responder side configuration. - These are the parameters affecting packets sent from server side.
[NOTE]
It is important to understand that this is *complimentary* to the client generator
-configured per profile - it only defines how the generator will be clustered.
+configured per profile - it only defines how the clients will be spread between clusters.
Let's look at an example.
@@ -881,11 +907,7 @@ $cat cap2/dns.yaml
clients_end : "16.0.0.255"
servers_start : "48.0.0.1"
servers_end : "48.0.0.255"
- clients_per_gb : 201
- min_clients : 101
dual_port_mask : "1.0.0.0"
- tcp_aging : 1
- udp_aging : 1
cap_info :
- name: cap2/dns.pcap
cps : 1.0
@@ -894,8 +916,10 @@ $cat cap2/dns.yaml
w : 1
----
-We want to create two clusters with 4 devices each.
-We also want to divide *80%* of the traffic to the upper cluster and *20%* to the lower cluster.
+We want to create two clusters with 4 and 3 devices respectively.
+We also want to send *80%* of the traffic to the upper cluster and *20%* to the lower cluster.
+We can specify to which DUT the packet will be sent by MAC address or IP. We will present a MAC
+based example, and then see how to change to be IP based.
We will create the following cluster configuration file.
@@ -911,13 +935,11 @@ We will create the following cluster configuration file.
#
# 'groups' - each client group must contain range of IPs
# and initiator and responder section
-# 'count' represents the number of different MACs
-# addresses in the group.
-#
-# initiator and responder can contain 'vlan', 'src_mac', 'dst_mac'
+# 'count' represents the number of different DUTs
+# in the group.
#
-# each group contains a double way VLAN configuration
+# 'true' means each group must contain VLAN configuration. 'false' means no VLAN config allowed.
vlan: true
groups:
@@ -929,7 +951,7 @@ groups:
dst_mac : "00:00:00:01:00:00"
responder :
vlan : 200
- dst_mac : "00:00:00:01:00:00"
+ dst_mac : "00:00:00:02:00:00"
count : 4
@@ -937,31 +959,91 @@ groups:
ip_end : 16.0.0.255
initiator :
vlan : 101
- dst_mac : "01:00:00:00:01:01"
+ dst_mac : "00:00:01:00:00:00"
responder:
vlan : 201
- dst_mac : "01:00:00:00:02:01"
+ dst_mac : "00:00:02:00:00:00"
- count : 4
+ count : 3
----
-The above configuration will divide the generator range of 255 clients to two clusters,
-each with 4 devices and VLAN in both directions.
+The above configuration will divide the generator range of 255 clients to two clusters. The range
+of IPs in all groups in the client config file together, must cover the entire range of client IPs
+from the traffic profile file.
-MACs will be allocated incrementaly, with a wrap around.
+MACs will be allocated incrementally, with a wrap around after ``count'' addresses.
e.g.
-* 16.0.0.1 --> 00:00:00:01:00:00
-* 16.0.0.2 --> 00:00:00:01:00:01
-* 16.0.0.3 --> 00:00:00:01:00:02
-* 16.0.0.4 --> 00:00:00:01:00:03
-* 16.0.0.5 --> 00:00:00:01:00:00
-* 16.0.0.6 --> 00:00:00:01:00:01
+*Initiator side: (packets with source in 16.x.x.x net)*
-and so on.
+* 16.0.0.1 -> 48.x.x.x - dst_mac: 00:00:00:01:00:00 vlan: 100
+* 16.0.0.2 -> 48.x.x.x - dst_mac: 00:00:00:01:00:01 vlan: 100
+* 16.0.0.3 -> 48.x.x.x - dst_mac: 00:00:00:01:00:02 vlan: 100
+* 16.0.0.4 -> 48.x.x.x - dst_mac: 00:00:00:01:00:03 vlan: 100
+* 16.0.0.5 -> 48.x.x.x - dst_mac: 00:00:00:01:00:00 vlan: 100
+* 16.0.0.6 -> 48.x.x.x - dst_mac: 00:00:00:01:00:01 vlan: 100
+
+*responder side: (packets with source in 48.x.x.x net)*
+
+* 48.x.x.x -> 16.0.0.1 - dst_mac(from responder) : "00:00:00:02:00:00" , vlan:200
+* 48.x.x.x -> 16.0.0.2 - dst_mac(from responder) : "00:00:00:02:00:01" , vlan:200
+
+and so on. +
+ +
+This means that the MAC addresses of DUTs must be changed to be sequential. Other option is to
+specify instead of ``dst_mac'', ip address, using ``next_hop''. +
+For example, config file first group will look like:
+
+[source,bash]
+----
+- ip_start : 16.0.0.1
+ ip_end : 16.0.0.204
+ initiator :
+ vlan : 100
+ next_hop : 1.1.1.1
+ src_ip : 1.1.1.100
+ responder :
+ vlan : 200
+ next_hop : 2.2.2.1
+ src_ip : 2.2.2.100
+
+ count : 4
+----
+
+In this case, TRex will try to resolve using ARP requests the addresses
+1.1.1.1, 1.1.1.2, 1.1.1.3, 1.1.1.4 (and the range 2.2.2.1-2.2.2.4). If not all IPs are resolved,
+TRex will exit with an error message. ``src_ip'' will be used for sending gratitues ARP, and
+for filling relevant fields in ARP request. If no ``src_ip'' given, TRex will look for source
+IP in the relevant port section in the platform config file (/etc/trex_cfg.yaml). If none is found, TRex
+will exit with an error message. +
+If client config file is given, the ``dest_mac'' and ``default_gw'' parameters from the platform config
+file are ignored.
+
+Now, streams will look like: +
+*Initiator side: (packets with source in 16.x.x.x net)*
+
+* 16.0.0.1 -> 48.x.x.x - dst_mac: MAC of 1.1.1.1 vlan: 100
+* 16.0.0.2 -> 48.x.x.x - dst_mac: MAC of 1.1.1.2 vlan: 100
+* 16.0.0.3 -> 48.x.x.x - dst_mac: MAC of 1.1.1.3 vlan: 100
+* 16.0.0.4 -> 48.x.x.x - dst_mac: MAC of 1.1.1.4 vlan: 100
+* 16.0.0.5 -> 48.x.x.x - dst_mac: MAC of 1.1.1.1 vlan: 100
+* 16.0.0.6 -> 48.x.x.x - dst_mac: MAC of 1.1.1.2 vlan: 100
+
+*responder side: (packets with source in 48.x.x.x net)*
+
+* 48.x.x.x -> 16.0.0.1 - dst_mac: MAC of 2.2.2.1 , vlan:200
+* 48.x.x.x -> 16.0.0.2 - dst_mac: MAC of 2.2.2.2 , vlan:200
+
+
+[NOTE]
+It is important to understand that the ip to MAC coupling (both with MAC based config or IP based)
+is done at the beginning and never changes. Meaning, for example, for the MAC case, packets
+with source IP 16.0.0.2 will always have VLAN 100 and dst MAC 00:00:00:01:00:01.
+Packets with destination IP 16.0.0.2 will always have VLAN 200 and dst MAC "00:00:00:02:00:01.
+This way, you can predict exactly which packet (and how many packets) will go to each DUT.
*Usage:*
@@ -2025,3 +2107,470 @@ sudo arp -s 172.168.0.100 <TRex side the NICs are not visible to ifconfig, run:
----
+=== Mellanox ConnectX-4 support
+
+anchor:connectx_support[]
+
+Mellanox ConnectX-4 adapter family supports 100/56/40/25/10 Gb/s Ethernet speeds.
+Its DPDK support is a bit different from Intel DPDK support, more information can be found link:http://dpdk.org/doc/guides/nics/mlx5.html[here].
+Intel NICs do not require additional kernel drivers (except for igb_uio which is already supported in most distributions). ConnectX-4 works on top of Infiniband API (verbs) and requires special kernel modules/user space libs.
+This means that it is required to install OFED package to be able to work with this NIC.
+Installing the full OFED package is the simplest way to make it work (trying to install part of the package can work too but didn't work for us).
+The advantage of this model is that you can control it using standard Linux tools (ethtool and ifconfig will work).
+The disadvantage is the OFED dependency.
+
+==== Installation
+
+==== Install Linux
+
+We tested the following distro with TRex and OFED. Others might work too.
+
+* CentOS 7.2
+
+Following distro was tested and did *not* work for us.
+
+* Fedora 21 (3.17.4-301.fc21.x86_64)
+* Ubuntu 14.04.3 LTS (GNU/Linux 3.19.0-25-generic x86_64) -- crash when RSS was enabled link:https://trex-tgn.cisco.com/youtrack/issue/trex-294[MLX RSS issue]
+
+==== Install OFED
+
+Information was taken from link:http://www.mellanox.com/page/products_dyn?product_family=26&mtag=linux_sw_drivers[Install OFED]
+
+* Download 3.4-2 OFED tar for your distro
+
+[IMPORTANT]
+=====================================
+The version must be *MLNX_OFED_LINUX-3.4-2*
+=====================================
+
+[IMPORTANT]
+=====================================
+Make sure you have an internet connection without firewalls for HTTPS/HTTP - required by yum/apt-get
+=====================================
+
+.Verify md5
+[source,bash]
+----
+$md5sum md5sum MLNX_OFED_LINUX-3.4-2.0.0.0-rhel7.2-x86_64.tgz
+58b9fb369d7c62cedbc855661a89a9fd MLNX_OFED_LINUX-3.4-2.0.0.0-rhel7.2-x86_64.tgz
+----
+
+.Open the tar
+[source,bash]
+----
+$tar -xvzf MLNX_OFED_LINUX-3.4-2.0.0.0-rhel7.2-x86_64.tgz
+$cd MLNX_OFED_LINUX-3.4-2.0.0.0-rhel7.2-x86_64
+----
+
+.Run Install script
+[source,bash]
+----
+$sudo ./mlnxofedinstall
+
+
+Log: /tmp/ofed.build.log
+Logs dir: /tmp/MLNX_OFED_LINUX.10406.logs
+
+Below is the list of MLNX_OFED_LINUX packages that you have chosen
+(some may have been added by the installer due to package dependencies):
+
+ofed-scripts
+mlnx-ofed-kernel-utils
+mlnx-ofed-kernel-dkms
+iser-dkms
+srp-dkms
+mlnx-sdp-dkms
+mlnx-rds-dkms
+mlnx-nfsrdma-dkms
+libibverbs1
+ibverbs-utils
+libibverbs-dev
+libibverbs1-dbg
+libmlx4-1
+libmlx4-dev
+libmlx4-1-dbg
+libmlx5-1
+libmlx5-dev
+libmlx5-1-dbg
+libibumad
+libibumad-static
+libibumad-devel
+ibacm
+ibacm-dev
+librdmacm1
+librdmacm-utils
+librdmacm-dev
+mstflint
+ibdump
+libibmad
+libibmad-static
+libibmad-devel
+libopensm
+opensm
+opensm-doc
+libopensm-devel
+infiniband-diags
+infiniband-diags-compat
+mft
+kernel-mft-dkms
+libibcm1
+libibcm-dev
+perftest
+ibutils2
+libibdm1
+ibutils
+cc-mgr
+ar-mgr
+dump-pr
+ibsim
+ibsim-doc
+knem-dkms
+mxm
+fca
+sharp
+hcoll
+openmpi
+mpitests
+knem
+rds-tools
+libdapl2
+dapl2-utils
+libdapl-dev
+srptools
+mlnx-ethtool
+libsdp1
+libsdp-dev
+sdpnetstat
+
+This program will install the MLNX_OFED_LINUX package on your machine.
+Note that all other Mellanox, OEM, OFED, or Distribution IB packages will be removed.
+Do you want to continue?[y/N]:y
+
+Checking SW Requirements...
+
+One or more required packages for installing MLNX_OFED_LINUX are missing.
+Attempting to install the following missing packages:
+autotools-dev tcl debhelper dkms tk8.4 libgfortran3 graphviz chrpath automake dpatch flex bison autoconf quilt m4 tcl8.4 libltdl-dev pkg-config pytho
+bxml2 tk swig gfortran libnl1
+
+..
+
+Removing old packages...
+Installing new packages
+Installing ofed-scripts-3.4...
+Installing mlnx-ofed-kernel-utils-3.4...
+Installing mlnx-ofed-kernel-dkms-3.4...
+
+Removing old packages...
+Installing new packages
+Installing ofed-scripts-3.4...
+Installing mlnx-ofed-kernel-utils-3.4...
+Installing mlnx-ofed-kernel-dkms-3.4...
+Installing iser-dkms-1.8.1...
+Installing srp-dkms-1.6.1...
+Installing mlnx-sdp-dkms-3.4...
+Installing mlnx-rds-dkms-3.4...
+Installing mlnx-nfsrdma-dkms-3.4...
+Installing libibverbs1-1.2.1mlnx1...
+Installing ibverbs-utils-1.2.1mlnx1...
+Installing libibverbs-dev-1.2.1mlnx1...
+Installing libibverbs1-dbg-1.2.1mlnx1...
+Installing libmlx4-1-1.2.1mlnx1...
+Installing libmlx4-dev-1.2.1mlnx1...
+Installing libmlx4-1-dbg-1.2.1mlnx1...
+Installing libmlx5-1-1.2.1mlnx1...
+Installing libmlx5-dev-1.2.1mlnx1...
+Installing libmlx5-1-dbg-1.2.1mlnx1...
+Installing libibumad-1.3.10.2.MLNX20150406.966500d...
+Installing libibumad-static-1.3.10.2.MLNX20150406.966500d...
+Installing libibumad-devel-1.3.10.2.MLNX20150406.966500d...
+Installing ibacm-1.2.1mlnx1...
+Installing ibacm-dev-1.2.1mlnx1...
+Installing librdmacm1-1.1.0mlnx...
+Installing librdmacm-utils-1.1.0mlnx...
+Installing librdmacm-dev-1.1.0mlnx...
+Installing mstflint-4.5.0...
+Installing ibdump-4.0.0...
+Installing libibmad-1.3.12.MLNX20160814.4f078cc...
+Installing libibmad-static-1.3.12.MLNX20160814.4f078cc...
+Installing libibmad-devel-1.3.12.MLNX20160814.4f078cc...
+Installing libopensm-4.8.0.MLNX20160906.32a95b6...
+Installing opensm-4.8.0.MLNX20160906.32a95b6...
+Installing opensm-doc-4.8.0.MLNX20160906.32a95b6...
+Installing libopensm-devel-4.8.0.MLNX20160906.32a95b6...
+Installing infiniband-diags-1.6.6.MLNX20160814.999c7b2...
+Installing infiniband-diags-compat-1.6.6.MLNX20160814.999c7b2...
+Installing mft-4.5.0...
+Installing kernel-mft-dkms-4.5.0...
+Installing libibcm1-1.0.5mlnx2...
+Installing libibcm-dev-1.0.5mlnx2...
+Installing perftest-3.0...
+Installing ibutils2-2.1.1...
+Installing libibdm1-1.5.7.1...
+Installing ibutils-1.5.7.1...
+Installing cc-mgr-1.0...
+Installing ar-mgr-1.0...
+Installing dump-pr-1.0...
+Installing ibsim-0.6...
+Installing ibsim-doc-0.6...
+Installing knem-dkms-1.1.2.90mlnx1...
+Installing mxm-3.5.220c57f...
+Installing fca-2.5.2431...
+Installing sharp-1.1.1.MLNX20160915.8763a35...
+Installing hcoll-3.6.1228...
+Installing openmpi-1.10.5a1...
+Installing mpitests-3.2.18...
+Installing knem-1.1.2.90mlnx1...
+Installing rds-tools-2.0.7...
+Installing libdapl2-2.1.9mlnx...
+Installing dapl2-utils-2.1.9mlnx...
+Installing libdapl-dev-2.1.9mlnx...
+Installing srptools-1.0.3...
+Installing mlnx-ethtool-4.2...
+Installing libsdp1-1.1.108...
+Installing libsdp-dev-1.1.108...
+Installing sdpnetstat-1.60...
+Selecting previously unselected package mlnx-fw-updater.
+(Reading database ... 70592 files and directories currently installed.)
+Preparing to unpack .../mlnx-fw-updater_3.4-1.0.0.0_amd64.deb ...
+Unpacking mlnx-fw-updater (3.4-1.0.0.0) ...
+Setting up mlnx-fw-updater (3.4-1.0.0.0) ...
+
+Added RUN_FW_UPDATER_ONBOOT=no to /etc/infiniband/openib.conf
+
+Attempting to perform Firmware update...
+Querying Mellanox devices firmware ...
+
+Device #1:
+
+ Device Type: ConnectX4
+ Part Number: MCX416A-CCA_Ax
+ Description: ConnectX-4 EN network interface card; 100GbE dual-port QSFP28; PCIe3.0 x16; ROHS R6
+ PSID: MT_2150110033
+ PCI Device Name: 03:00.0
+ Base GUID: 248a07030014fc60
+ Base MAC: 0000248a0714fc60
+ Versions: Current Available
+ FW 12.16.1006 12.17.1010
+ PXE 3.4.0812 3.4.0903
+
+ Status: Update required
+
+
+Found 1 device(s) requiring firmware update...
+
+Device #1: Updating FW ... Done
+
+Restart needed for updates to take effect.
+Log File: /tmp/MLNX_OFED_LINUX.16084.logs/fw_update.log
+Please reboot your system for the changes to take effect.
+Device (03:00.0):
+ 03:00.0 Ethernet controller: Mellanox Technologies MT27620 Family
+ Link Width: x16
+ PCI Link Speed: 8GT/s
+
+Device (03:00.1):
+ 03:00.1 Ethernet controller: Mellanox Technologies MT27620 Family
+ Link Width: x16
+ PCI Link Speed: 8GT/s
+
+Installation passed successfully
+To load the new driver, run:
+/etc/init.d/openibd restart
+-----
+
+
+.Reboot
+[source,bash]
+----
+$sudo reboot
+----
+
+
+.After reboot
+[source,bash]
+----
+$ibv_devinfo
+hca_id: mlx5_1
+ transport: InfiniBand (0)
+ fw_ver: 12.17.1010 << 12.17.00
+ node_guid: 248a:0703:0014:fc61
+ sys_image_guid: 248a:0703:0014:fc60
+ vendor_id: 0x02c9
+ vendor_part_id: 4115
+ hw_ver: 0x0
+ board_id: MT_2150110033
+ phys_port_cnt: 1
+ Device ports:
+ port: 1
+ state: PORT_DOWN (1)
+ max_mtu: 4096 (5)
+ active_mtu: 1024 (3)
+ sm_lid: 0
+ port_lid: 0
+ port_lmc: 0x00
+ link_layer: Ethernet
+
+hca_id: mlx5_0
+ transport: InfiniBand (0)
+ fw_ver: 12.17.1010
+ node_guid: 248a:0703:0014:fc60
+ sys_image_guid: 248a:0703:0014:fc60
+ vendor_id: 0x02c9
+ vendor_part_id: 4115
+ hw_ver: 0x0
+ board_id: MT_2150110033
+ phys_port_cnt: 1
+ Device ports:
+ port: 1
+ state: PORT_DOWN (1)
+ max_mtu: 4096 (5)
+ active_mtu: 1024 (3)
+ sm_lid: 0
+ port_lid: 0
+ port_lmc: 0x00
+ link_layer: Ethernet
+
+----
+
+.ibdev2netdev
+[source,bash]
+-----
+$ibdev2netdev
+mlx5_0 port 1 ==> eth6 (Down)
+mlx5_1 port 1 ==> eth7 (Down)
+-----
+
+.find the ports
+[source,bash]
+-----
+
+ $sudo ./dpdk_setup_ports.py -t
+ +----+------+---------++---------------------------------------------
+ | ID | NUMA | PCI || Name | Driver |
+ +====+======+=========++===============================+===========+=
+ | 0 | 0 | 06:00.0 || VIC Ethernet NIC | enic |
+ +----+------+---------++-------------------------------+-----------+-
+ | 1 | 0 | 07:00.0 || VIC Ethernet NIC | enic |
+ +----+------+---------++-------------------------------+-----------+-
+ | 2 | 0 | 0a:00.0 || 82599ES 10-Gigabit SFI/SFP+ Ne| ixgbe |
+ +----+------+---------++-------------------------------+-----------+-
+ | 3 | 0 | 0a:00.1 || 82599ES 10-Gigabit SFI/SFP+ Ne| ixgbe |
+ +----+------+---------++-------------------------------+-----------+-
+ | 4 | 0 | 0d:00.0 || Device 15d0 | |
+ +----+------+---------++-------------------------------+-----------+-
+ | 5 | 0 | 10:00.0 || I350 Gigabit Network Connectio| igb |
+ +----+------+---------++-------------------------------+-----------+-
+ | 6 | 0 | 10:00.1 || I350 Gigabit Network Connectio| igb |
+ +----+------+---------++-------------------------------+-----------+-
+ | 7 | 1 | 85:00.0 || 82599ES 10-Gigabit SFI/SFP+ Ne| ixgbe |
+ +----+------+---------++-------------------------------+-----------+-
+ | 8 | 1 | 85:00.1 || 82599ES 10-Gigabit SFI/SFP+ Ne| ixgbe |
+ +----+------+---------++-------------------------------+-----------+-
+ | 9 | 1 | 87:00.0 || MT27700 Family [ConnectX-4] | mlx5_core | #<1>
+ +----+------+---------++-------------------------------+-----------+-
+ | 10 | 1 | 87:00.1 || MT27700 Family [ConnectX-4] | mlx5_core | #<2>
+ +----+------+---------++---------------------------------------------
+-----
+<1> ConnectX-4 port 0
+<2> ConnectX-4 port 1
+
+
+.Config file example
+[source,bash]
+-----
+### Config file generated by dpdk_setup_ports.py ###
+
+ - port_limit: 2
+ version: 2
+ interfaces: ['87:00.0', '87:00.1']
+ port_info:
+ - ip: 1.1.1.1
+ default_gw: 2.2.2.2
+ - ip: 2.2.2.2
+ default_gw: 1.1.1.1
+
+ platform:
+ master_thread_id: 0
+ latency_thread_id: 1
+ dual_if:
+ - socket: 1
+ threads: [8,9,10,11,12,13,14,15,24,25,26,27,28,29,30,31]
+-----
+
+
+
+
+==== TRex specific implementation details
+
+TRex uses flow director filter to steer specific packets to specific queues.
+To support that, we change IPv4.TOS/Ipv6.TC LSB to *1* for packets we want to handle by software (Other packets will be dropped). So latency packets will have this bit turned on (This is true for all NIC types, not only for ConnectX-4).
+This means taht if the DUT for some reason clears this bit (change TOS LSB to 0, e.g. change it from 0x3 to 0x2 for example) some TRex features (latency measurement for example) will not work properly.
+
+==== Which NIC to buy?
+
+NIC with two ports will work better from performance prospective, so it is better to have MCX456A-ECAT(dual 100gb ports) and *not* the MCX455A-ECAT (single 100gb port).
+
+==== Limitation/Issues
+
+* Stateless mode ``per stream statistics'' feature is handled in software (No hardware support like in X710 card).
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-260[64B performance issue]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-261[Latency issue]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-262[Statful RX out of order]
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-273[Fedora 21 & OFED 3.4.1]
+
+
+==== Performance Cycles/Packet ConnectX-4 vs Intel XL710
+
+For TRex version v2.11, these are the comparison results between XL710 and ConnectX-4 for various scenarios.
+
+.Stateless MPPS/Core [Preliminary]
+image:images/xl710_vs_mlx5_64b.png[title="Stateless 64B"]
+
+.Stateless Gb/Core [Preliminary]
+image:images/xl710_vs_mlx5_var_size.png[title="Stateless variable size packet"]
+
+*Comments*::
+
+1. For Stateless 64B profiles, ConnectX-4 uses 50-90% more CPU cycles per packet (it is actually even more because there is the TRex scheduler overhead) - it means that in worst case scenario, you will need x2 CPU for the same total MPPS.
+2. For Stateless/Stateful 256B profiles, ConnectX-4 uses half of the CPU cycles per packet. ConnectX-4 probably can handle in a better way chained mbufs (scatter gather).
+3. In average stateful senario, ConnectX-4 is slightly better.
+4. MLX5 can reach ~90MPPS while XL710 is limited to 35MPPS.
+
+
+[NOTE]
+=====================================
+There is a task to automate the production of thess reports
+=====================================
+
+==== Troubleshooting
+
+* Before running TRex make sure the commands `ibv_devinfo` and `ibdev2netdev` present the NICS
+* `ifconfig` should work too, you need to be able to ping from those ports
+* run TRex server with '-v 7' for example `$sudo ./t-rex-64 -i -v 7`
+
+
+=== Cisco VIC support
+
+anchor:ciscovic_support[]
+
+* Supported from TRex version v2.12
+* Only 1300 series Cisco adapter supported
+* Must have VIC firmware version 2.0(13) for UCS C-series servers. Will be GA in Febuary 2017.
+* Must have VIC firmware version 3.1(2) for blade servers (which supports more filtering capabilities).
+* The feature can be enabled via Cisco CIMC or USCM with the 'advanced filters' radio button. When enabled, these additional flow director modes are available:
+ RTE_ETH_FLOW_NONFRAG_IPV4_OTHER
+ RTE_ETH_FLOW_NONFRAG_IPV4_SCTP
+ RTE_ETH_FLOW_NONFRAG_IPV6_UDP
+ RTE_ETH_FLOW_NONFRAG_IPV6_TCP
+ RTE_ETH_FLOW_NONFRAG_IPV6_SCTP
+ RTE_ETH_FLOW_NONFRAG_IPV6_OTHER
+
+
+==== Limitations/Issues
+
+* Stateless mode ``per stream statistics'' feature is handled in software (No hardware support like in X710 card).
+* link:https://trex-tgn.cisco.com/youtrack/issue/trex-272[QSFP+ issue]
+
+
+
diff --git a/doc/trex_faq.asciidoc b/doc/trex_faq.asciidoc
index 44f4f237..eb6938a1 100644
--- a/doc/trex_faq.asciidoc
+++ b/doc/trex_faq.asciidoc
@@ -77,13 +77,18 @@ Limitations:
2. We have regression tests in our lab for each recommended NIC. We do not claim to support NICs we do not have in our lab.
==== Is Cisco VIC supported?
-No. Currently its DPDK driver does not support the capabilities needed to run TRex.
+Yes. Since version 2.12, with link:trex_manual.html#_cisco_vic_support[these limitations]. Especially note that
+a new firmware version is needed.
==== Is 100Gbs NIC QSFP+ supported?
Not yet. Support for FM10K and Mellanox Connectx5 is under development.
==== Is there a GUI?
-TRex team is not developing it. Have a look link:https://groups.google.com/forum/#!searchin/trex-tgn/sari%7Csort:relevance/trex-tgn/R92-N2Yjy2Q/DIUe06YCBgAJ[here] for TRex Stateless mode GUI from Exalt company.
+TRex team is not developing it.
+You can find GUI for statless mode developed by Exalt company in
+link:https://github.com/exalt-tech/trex-stateless-gui#trex-stateless-gui-beta[this link]. +
+There is also work in progress for packet editor (not released yet) in link:https://github.com/cisco-system-traffic-generator/trex-packet-editor[here]. +
+New stateless GUI features are developed in link:https://github.com/cisco-system-traffic-generator/trex-stateless-gui[here]. This is also work in progress, not released yet.
==== What is the maximum number of ports per TRex application?
12 ports
@@ -118,7 +123,10 @@ This is still better than the Intel x520 (82559 based) which can reach ~30MPPS f
==== I have XL710 NIC with 2x40Gb/sec ports and I can not get line rate
XL710-da2 with 2 40G ports can reach maximum of 40MPPS/50Gb (total for all ports) and not 60MPPS with small packets (64B)
Intel had in mind redundancy use case when they produced a two port NIC. Card was not intended to reach 80G line rate.
-see link:trex_stateless_bench.html[xl710_benchmark.html] for more info
+see link:trex_stateless_bench.html[xl710_benchmark.html] for more info.
+
+==== How does TRex calculate the throughput and where is this part of source code located?
+There is good answer in the mailing list link:https://groups.google.com/forum/#!topic/trex-tgn/Hk9IFJJ0KNs[here].
==== I want to contribute to the project
You have several ways you can help: +
@@ -234,6 +242,40 @@ Default maximum supported flows is 1M (From TRex prespective. DUT might have muc
To increase the number of supported active flows, you should add ``dp_flows'' arg in config file ``memory'' section.
Look link:trex_manual.html#_memory_section_configuration[here] for more info.
+.example of CFG file
+[source,bash]
+----
+
+ - port_limit : 4
+ version : 2
+ interfaces : ["02:00.0","02:00.1","84:00.0","84:00.1"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status
+ memory :
+ dp_flows : 10048576 #<1>
+
+----
+<1> more flows 10Mflows
+
+==== Loading a big YAML file raise an error no enough memory for specific pool 2048?
+
+You should increse the pool with that raise an error, for example in case of 2048
+
+.example of CFG file
+[source,bash]
+----
+
+ - port_limit : 4
+ version : 2
+ interfaces : ["02:00.0","02:00.1","84:00.0","84:00.1"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status
+ memory :
+ traffic_mbuf_2048 : 8000 #<1>
+
+----
+<1> for mbuf for 2038
+
+You can run TRex with `-v 7` to verify that the configuration has an effect
+
+
+
==== I want to have more active flows on the DUT, how can I do this?
After stretching TRex to its maximum CPS capacity, consider the following: DUT will have much more active flows in case of a UDP flow due to the nature of aging (DUT does not know when the flow ends while TRex knows).
In order to artificialy increse the length of the active flows in TRex, you can config larger IPG in the YAML file. This will cause each flow to last longer. Alternatively, you can increase IPG in your PCAP file as well.
@@ -438,6 +480,12 @@ You can see example for defining and using tunables link:trex_stateless.html#_tu
Correct. We use NIC capabilities for counting the packets or directing them to be handled by software. Each NIC has its own capabilities. Look link:trex_stateless.html#_tutorial_per_stream_statistics[here] for per stream statistics and link:trex_stateless.html#_tutorial_per_stream_latency_jitter_packet_errors[here] for latency details.
+===== I use per stream statistics on x710/xl710 card and rx-bps counter from python API (and rx bytes releated counters in console)
+always show N/A
+
+This is because on these card types, we use hardware counters (as opposed to counting in software in other card types).
+While this allows for counting of full 40G line rate streams, this does not allow for counting bytes (only packet
+count available), because the hardware on these NICs lacks this support.
diff --git a/doc/trex_index.asciidoc b/doc/trex_index.asciidoc
index 454e21ec..f8fb2b7a 100644
--- a/doc/trex_index.asciidoc
+++ b/doc/trex_index.asciidoc
@@ -16,8 +16,8 @@ include::trex_ga.asciidoc[]
link:trex_preso.html[Stateful]
| Stateful DPDK |
link:http://www.slideshare.net/harryvanhaaren/trex-traffig-gen-hanoch-haim[DPDK summit]
-| *New* Stateless support |
-http://www.slideshare.net/HanochHaim/trex-realistic-traffic-generator-stateless-support[Stateless support]
+| Stateless support |
+http://www.slideshare.net/HanochHaim/trex-realistic-traffic-generator-stateless-support[Stateless support] link:https://www.youtube.com/watch?v=hoCJ9xyvaw0[short video]
|=================
@@ -26,7 +26,7 @@ http://www.slideshare.net/HanochHaim/trex-realistic-traffic-generator-stateless-
[options="header",cols="<4,a"]
|=================
| Description | Name
-| FAQ |
+| Frequently Asked Questions |
link:trex_faq.html[FAQ]
| Installation Guide |
link:trex_manual.html#_download_and_installation[Installation]
@@ -74,6 +74,8 @@ link:../release/[pkgs]
link:../client_gui/[stateful GUI]
| XL710 Statless performance |
link:trex_stateless_bench.html[xl710_benchmark.html]
+| TRex performance trend per platform/NIC |
+link:trex_analytics.html[trex_perf_trend.html]
|=================
== For Developers
diff --git a/doc/trex_stateless.asciidoc b/doc/trex_stateless.asciidoc
index 419df9b3..848c9ecf 100755
--- a/doc/trex_stateless.asciidoc
+++ b/doc/trex_stateless.asciidoc
@@ -38,7 +38,7 @@ endif::backend-xhtml11[]
This document assumes basic knowledge of TRex, and assumes that TRex is installed and configured.
For information, see the link:trex_manual.html[manual], especially the material up to the link:trex_manual.html#_basic_usage[Basic Usage] section.
-== Stateless support (Beta stage)
+== Stateless support
=== High level functionality
// maybe Feature overview
@@ -76,11 +76,6 @@ The following example shows three streams configured for Continuous, Burst, and
image::images/stl_streams_example_02.png[title="Example of multiple streams",align="left",width={p_width}, link="images/stl_streams_example_02.png"]
-==== High level functionality - near future
-
-// "near future" and "roadmap" (below) are ~ same. Typically, Cisco does not document features before they're ready, but open source is a little different. We might want to find a better place to put the roadmap for the future - maybe a separate document.
-
-* ARP emulation - learn server MAC. Support unlimited MAC addresses per port.
==== High level functionality - Roadmap for future development
@@ -239,6 +234,367 @@ If you are confused you probably need Stateless. :-)
| /automation/trex_control_plane/stl/examples | Stateless Examples
|=============================
+=== Port Layer Mode Configuration
+
+TRex ports can operate in two different mutual exclusive modes:
+
+* *Layer 2 mode* - MAC level configuration
+* *Layer 3 mode* - IPv4/IPv6 configuration
+
+When configuring a port for L2 mode, it is only required to provide
+the destination MAC address for the port (Legacy mode previous to v2.12 version).
+
+When configuring a port for L3, it is required to provide both
+source IPv4/IPv6 address and a IPv4/IPv6 destination address.
+
+As an intergral part of configuring L3, the client will try to ARP resolve the
+destination address and automatically configure the correct destination MAC.
+(instead of sending ARP request when starting traffic)
+
+[NOTE]
+While in L3 mode, TRex server will generate *gratuitous ARP* packets to make sure
+that no ARP timeout on the DUT/router will result in a faliure of the test.
+
+.*Example of configuring L2 mode*
+[source,bash]
+----
+
+trex>service
+
+trex>l2 --help
+usage: port [-h] --port PORT --dst DST_MAC
+
+Configures a port in L2 mode
+
+optional arguments:
+ -h, --help show this help message and exit
+ --port PORT, -p PORT source port for the action
+ --dst DST_MAC Configure destination MAC address
+
+
+trex(service)>l2 -p 0 --dst 6A:A7:B5:3A:4E:FF
+
+Setting port 0 in L2 mode: [SUCCESS]
+
+trex>service --off
+
+----
+
+
+.*Example of configuring L2 mode- Python API*
+[source,Python]
+----
+ client.set_service_mode(port = 0, enabled = True)
+
+ client.set_l2_mode(port = 0, dst_mac = "6A:A7:B5:3A:4E:FF")
+
+ client.set_service_mode(port = 0, enabled = False)
+
+----
+
+
+.*Example of configuring L3 mode- Console*
+[source,bash]
+----
+
+trex>service
+
+
+trex(service)>l3 --help
+usage: port [-h] --port PORT --src SRC_IPV4 --dst DST_IPV4
+
+Configures a port in L3 mode
+
+optional arguments:
+ -h, --help show this help message and exit
+ --port PORT, -p PORT source port for the action
+ --src SRC_IPV4 Configure source IPv4 address
+ --dst DST_IPV4 Configure destination IPv4 address
+
+trex(service)>l3 -p 0 --src 1.1.1.2 --dst 1.1.1.1
+
+Setting port 0 in L3 mode: [SUCCESS]
+
+
+ARP resolving address '1.1.1.1': [SUCCESS]
+
+trex>service --off
+
+----
+
+
+.*Example of configuring L3 mode - Python API*
+[source,python]
+----
+
+client.set_service_mode(port = 0, enabled = True)
+
+client.set_l3_mode(port = 0, src_ipv4 = '1.1.1.2', dst_ipv4 = '1.1.1.1')
+
+client.set_service_mode(port = 0, enabled = False)
+
+----
+
+=== Port Service Mode
+
+In 'normal operation mode', to preserve high speed processing of packets,
+TRex ignores most of the RX traffic, with the exception of counting/statistic and handling
+latency flows.
+
+
+In the following diagram it is illustrated how RX packets are handled.
+Only a portion is forwarded to the RX handling module and none of forward back
+to the Python client.
+
+image::images/port_normal_mode.png[title="Port Under Normal Mode",align="left",width={p_width}, link="images/port_normal_mode.png"]
+
+
+
+We provide another mode called 'service mode' in which a port will respond to ping, ARP requests
+and also provide a capabality in this mode to forward packets to the Python control plane for
+applying full duplex protocols (DCHP, IPv6 neighboring and etc.)
+
+The following diagram illustrates of packets can be forwarded back to the Python client
+
+image::images/port_service_mode.png[title="Port Under Service Mode",align="left",width={p_width}, link="images/port_service_mode.png"]
+
+In this mode, it is possible to write python plugins for emulation (e.g. IPV6 ND/DHCP) to prepare the setup and then move to normal mode for high speed testing
+
+
+*Example Of Switcing Between 'Service' And 'Normal' Mode*
+[source,bash]
+----
+
+trex(service)>service --help
+usage: service [-h] [--port PORTS [PORTS ...] | -a] [--off]
+
+Configures port for service mode. In service mode ports will reply to ARP,
+PING and etc.
+
+optional arguments:
+ -h, --help show this help message and exit
+ --port PORTS [PORTS ...], -p PORTS [PORTS ...]
+ A list of ports on which to apply the command
+ -a Set this flag to apply the command on all available
+ ports
+ --off Deactivates services on port(s)
+
+
+trex>service
+
+Enabling service mode on port(s) [0, 1]: [SUCCESS]
+
+trex(service)>service --off
+
+Disabling service mode on port(s) [0, 1]: [SUCCESS]
+
+----
+
+.*Example Of Switcing Between 'Service' And 'Normal' Mode-API*
+[source,Python]
+----
+
+ client.set_service_mode(ports = [0, 1], enabled = True)
+
+ client.set_service_mode(ports = [0, 1], enabled = False)
+
+----
+
+==== ARP / ICMP response
+[IMPORTANT]
+Only while in service mode, ports will reply to ICMP echo requests and ARP requests.
+
+
+=== Neighboring Protocols
+As mentioned, in order to preserve high speed traffic generation,
+TRex handles neighboring protocols in pre test phase.
+
+A test that requires running a neighboring protocol should first move
+to 'service mode', execute the required steps in Python, switch back to 'normal mode'
+and start the actual test.
+
+==== ARP
+A basic neighboring protocol that is provided as part of TRex is ARP.
+
+For example, let's take a look at the following setup:
+
+image::images/router_arp.png[title="Router ARP",align="left",width={p_width}, link="images/router_arp.png"]
+
+[source,bash]
+----
+
+trex>service #<1>
+
+Enabling service mode on port(s) [0, 1]: [SUCCESS]
+
+trex(service)>portattr --port 0
+
+ port | 0 |
+ ------------------------------------------
+ driver | rte_ixgbe_pmd |
+ description | 82599EB 10-Gigabit |
+ link status | UP |
+ link speed | 10 Gb/s |
+ port status | IDLE |
+ promiscuous | off |
+ flow ctrl | none |
+ -- | |
+ src IPv4 | - |
+ src MAC | 00:00:00:01:00:00 |
+ --- | |
+ Destination | 00:00:00:01:00:00 |
+ ARP Resolution | - |
+ ---- | |
+ PCI Address | 0000:03:00.0 |
+ NUMA Node | 0 |
+ ----- | |
+ RX Filter Mode | hardware match |
+ RX Queueing | off |
+ RX sniffer | off |
+ Grat ARP | off |
+
+
+trex(service)>l3 -p -s 1.1.1.1 -d 1.1.1.2 #<2>
+
+trex(service)>arp -p 0 1 #<3>
+
+Resolving destination on port(s) [0, 1]: [SUCCESS]
+
+
+Port 0 - Recieved ARP reply from: 1.1.1.1, hw: d0:d0:fd:a8:a1:01
+Port 1 - Recieved ARP reply from: 1.1.2.1, hw: d0:d0:fd:a8:a1:02
+
+trex(service)>service --off #<4>
+
+----
+<1> Enable service mode
+<2> Set IPv4/default gateway. it will resolve the arp
+<3> repeat ARP resolution
+<4> exist from service mode
+
+
+
+to revert back to MAC address mode (without ARP resolution) you do the following
+
+.Disable L3 mode
+[source,bash]
+----
+
+trex>l2 -p 0 --dst 00:00:00:01:00:00 #<1>
+
+trex>portattr --port 0
+
+ port | 0 |
+ ------------------------------------------
+ driver | rte_ixgbe_pmd |
+ description | 82599EB 10-Gigabit |
+ link status | UP |
+ link speed | 10 Gb/s |
+ port status | IDLE |
+ promiscuous | off |
+ flow ctrl | none |
+ -- | |
+ src IPv4 | - |
+ src MAC | 00:00:00:01:00:00 |
+ --- | |
+ Destination | 00:00:00:01:00:00 |
+ ARP Resolution | - |
+ ---- | |
+ PCI Address | 0000:03:00.0 |
+ NUMA Node | 0 |
+ ----- | |
+ RX Filter Mode | hardware match |
+ RX Queueing | off |
+ RX sniffer | off |
+ Grat ARP | off |
+
+----
+<1> disable service mode
+
+
+
+.Python API:
+[source,python]
+----
+
+client.set_service_mode(ports = [0, 1], enabled = True) <1>
+
+# configure port 0, 1 to Layer 3 mode
+client.set_l3_mode(port = 0, src_ipv4 = '1.1.1.2', dst_ipv4 = '1.1.1.2') <2>
+client.set_l3_mode(port = 1, src_ipv4 = '1.1.2.2', dst_ipv4 = '1.1.2.1')
+
+# ARP resolve ports 0, 1
+c.resolve(ports = [0, 1])
+
+client.set_service_mode(ports = [0, 1], enabled = False) <3>
+
+----
+<1> Enable service mode
+<2> configure IPv4 and Default Gateway
+<3> Disable service mode
+
+==== ICMP
+
+Another basic protocol provided with TRex is ICMP.
+It is possible, under service mode to ping the DUT or even a TRex port
+from the console / API.
+
+.TRex Console
+[source,bash]
+----
+
+trex(service)>ping --help
+usage: ping [-h] --port PORT -d PING_IPV4 [-s PKT_SIZE] [-n COUNT]
+
+pings the server / specific IP
+
+optional arguments:
+ -h, --help show this help message and exit
+ --port PORT, -p PORT source port for the action
+ -d PING_IPV4 which IPv4 to ping
+ -s PKT_SIZE packet size to use
+ -n COUNT, --count COUNT
+ How many times to ping [default is 5]
+
+trex(service)>ping -p 0 -d 1.1.2.2
+
+Pinging 1.1.2.2 from port 0 with 64 bytes of data:
+Reply from 1.1.2.2: bytes=64, time=27.72ms, TTL=127
+Reply from 1.1.2.2: bytes=64, time=1.40ms, TTL=127
+Reply from 1.1.2.2: bytes=64, time=1.31ms, TTL=127
+Reply from 1.1.2.2: bytes=64, time=1.78ms, TTL=127
+Reply from 1.1.2.2: bytes=64, time=1.95ms, TTL=127
+
+----
+
+
+
+.Python API
+[source,python]
+----
+
+# move to service mode
+client.set_service_mode(ports = ports, enabled = True)
+
+# configure port 0, 1 to Layer 3 mode
+client.set_l3_mode(port = 0, src_ipv4 = '1.1.1.2', dst_ipv4 = '1.1.1.1')
+client.set_l3_mode(port = 1, src_ipv4 = '1.1.2.2', dst_ipv4 = '1.1.2.1')
+
+# ping port 1 from port 0 through the router
+client.ping_ip(src_port = 0, dst_ipv4 = '1.1.2.2', pkt_size = 64) <1>
+
+# disable service mode
+client.set_service_mode(enabled = False)
+
+----
+<1> Check connectivity
+
+
+==== IPv6 ND/DHCP client
+
+in progress
+
+
=== Tutorials
The tutorials in this section demonstrate basic TRex *stateless* use cases. Examples include common and moderately advanced TRex concepts.
@@ -2522,6 +2878,8 @@ trex>
** IPv6 with one VLAN tag (except 82599 which does not support this type of packet)
* Maximum number of concurrent streams (with different pg_id) on which statistics may be collected: 127
+* On x710/xl710 cards, all rx bytes counters (rx-bps, rx-bps-L1, ...) are not supported. This is because we use hardware
+counters which support only packets count on these cards.
Two examples follow, one using the console and the other using the Python API.
diff --git a/doc/trex_toc.asciidoc b/doc/trex_toc.asciidoc
index a41d707f..eecb883f 100644
--- a/doc/trex_toc.asciidoc
+++ b/doc/trex_toc.asciidoc
@@ -24,7 +24,7 @@ ifdef::backend-xhtml11[]
<!-- load the theme CSS file -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" rel="stylesheet"/>
- <link href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" rel="stylesheet" />
+ <link href="https://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" rel="stylesheet" />
<!-- include the jQuery library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js">
diff --git a/doc/visio_drawings/illustrations_stateless.vsd b/doc/visio_drawings/illustrations_stateless.vsd
index 9a979a7f..f6782400 100755
--- a/doc/visio_drawings/illustrations_stateless.vsd
+++ b/doc/visio_drawings/illustrations_stateless.vsd
Binary files differ
diff --git a/doc/visio_drawings/topology.vsd b/doc/visio_drawings/topology.vsd
new file mode 100644
index 00000000..d076023e
--- /dev/null
+++ b/doc/visio_drawings/topology.vsd
Binary files differ
diff --git a/doc/waf-1.9.3 b/doc/waf-1.9.3
deleted file mode 100755
index 7ad6ae70..00000000
--- a/doc/waf-1.9.3
+++ /dev/null
@@ -1,170 +0,0 @@
-#!/usr/bin/env python
-# encoding: ISO8859-1
-# Thomas Nagy, 2005-2016
-#
-"""
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-"""
-
-import os, sys, inspect
-
-VERSION="1.9.3"
-REVISION="826456a3e8d1d27787c240c6ba69d280"
-GIT="b022313c197614245fae732c0dd1d8167b9ee5dd"
-INSTALL=''
-C1='#)'
-C2='#%'
-C3='#$'
-cwd = os.getcwd()
-join = os.path.join
-
-
-WAF='waf'
-def b(x):
- return x
-if sys.hexversion>0x300000f:
- WAF='waf3'
- def b(x):
- return x.encode()
-
-def err(m):
- print(('\033[91mError: %s\033[0m' % m))
- sys.exit(1)
-
-def unpack_wafdir(dir, src):
- f = open(src,'rb')
- c = 'corrupt archive (%d)'
- while 1:
- line = f.readline()
- if not line: err('run waf-light from a folder containing waflib')
- if line == b('#==>\n'):
- txt = f.readline()
- if not txt: err(c % 1)
- if f.readline() != b('#<==\n'): err(c % 2)
- break
- if not txt: err(c % 3)
- txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
-
- import shutil, tarfile
- try: shutil.rmtree(dir)
- except OSError: pass
- try:
- for x in ('Tools', 'extras'):
- os.makedirs(join(dir, 'waflib', x))
- except OSError:
- err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
-
- os.chdir(dir)
- tmp = 't.bz2'
- t = open(tmp,'wb')
- try: t.write(txt)
- finally: t.close()
-
- try:
- t = tarfile.open(tmp)
- except:
- try:
- os.system('bunzip2 t.bz2')
- t = tarfile.open('t')
- tmp = 't'
- except:
- os.chdir(cwd)
- try: shutil.rmtree(dir)
- except OSError: pass
- err("Waf cannot be unpacked, check that bzip2 support is present")
-
- try:
- for x in t: t.extract(x)
- finally:
- t.close()
-
- for x in ('Tools', 'extras'):
- os.chmod(join('waflib',x), 493)
-
- if sys.hexversion<0x300000f:
- sys.path = [join(dir, 'waflib')] + sys.path
- import fixpy2
- fixpy2.fixdir(dir)
-
- os.remove(tmp)
- os.chdir(cwd)
-
- try: dir = unicode(dir, 'mbcs')
- except: pass
- try:
- from ctypes import windll
- windll.kernel32.SetFileAttributesW(dir, 2)
- except:
- pass
-
-def test(dir):
- try:
- os.stat(join(dir, 'waflib'))
- return os.path.abspath(dir)
- except OSError:
- pass
-
-def find_lib():
- src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
- base, name = os.path.split(src)
-
- #devs use $WAFDIR
- w=test(os.environ.get('WAFDIR', ''))
- if w: return w
-
- #waf-light
- if name.endswith('waf-light'):
- w = test(base)
- if w: return w
- err('waf-light requires waflib -> export WAFDIR=/folder')
-
- dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
- for i in (INSTALL,'/usr','/usr/local','/opt'):
- w = test(i + '/lib/' + dirname)
- if w: return w
-
- #waf-local
- dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
- w = test(dir)
- if w: return w
-
- #unpack
- unpack_wafdir(dir, src)
- return dir
-
-wafdir = find_lib()
-sys.path.insert(0, wafdir)
-
-if __name__ == '__main__':
-
- from waflib import Scripting
- Scripting.waf_entry_point(cwd, VERSION, wafdir)
-
-#==>
-#BZh91AY&SY^:Z})2ÿÿ±#$Âÿÿÿÿÿÿÿÿÿÿÿ]á õ#$8E„xaöÜ÷½¾Ï8€#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$¯}W#%÷;©™‘U¶Ò*[·]ö}÷i«n»¶ú8¥9ŠÓ}öwË:,×¾ä懻vÕÜûÞìÊåï«ï½žžöíží‡må… h­»»Í‘&°t ö^î=;>í{í¢_CO}<¸ÛÝÙ›ï>n8>¯[Ÿ]=ãT$ìß{ß}óï†ÎÞ7¾Ñ×ß{íyéÀ÷`ïw7w>»ì#$#$@#$Ÿf€ô£§#$m÷²€uàæÝ·p#$õÞÒͧCÐöòíkf©­Ðjû½×#C{y zØBöîÛ`7³p …D¥Dª=ì{e’B%t(H•Q@÷p:Rß{¥ç¶6±Æœû«=i{…e»QÛÛxÔvÊ¢•¶îu×וèûç½^#$Ùçœë«Íõ»ìhÝâz={0ªM½k˜õÕîî{ëdäåžÝÓ}:vßq÷²ÝÝ^×ßo·×ÞîÏ.‹m}ìa°îz:U­Ølì蹺I»»Ü÷·yêÆ:ëBîÑÝ•†ÚÙ’DE»½ÞÓƒÖ j¨-;`)èy¡{“¥éæ·¶ô[ÝÝtjT}¸;ëÓ统më >€ÑC­m÷Þv+뺷 lÀ »ëm€ß]xö‚•íÊà-÷Ýï{¼¹ÐPûÞö¼çÆîÎY]4Tû½ÞMw7Kì¹Ùèy3mºãÝß]õçVÕˆ©»·vÖ2ÙÖïmímÍíÝwtÜç·{ÑÞóW'‘ÎíéuÜ¡k®w ‹í©>ŸlwgÄ÷m³Õæ#%÷¶¹§kŸyº{Г*ðªÇϱ÷cÞͶï½ÓÝ}îíµçg>òöì”ûÜ÷¶û²œ^öTæÛ–v­Ýi¼µ€Òw¼ïAööÁ÷yä9Àz‚Š®±Ðj©ØSCs;´1‘îw¬î¶÷*Ü4è=Ærw{3Ó]W©º®Qpj{qïe#ºòØ4 ÝîÛt·\ï#$on: #$×½×¼îõµÞ=×î¤ãÓíïfÉ뻎÷Ÿ{Ýh¹Ñ·#%Ö ›Ó]±GÍ„’×ÜRz­Y–ûÝ:}q]ô;Û/-÷Ýsî÷{›O€’ :=æ·weí«“1^Ï/Ro`´s#AŸmÈû›îÝ“f¯;«­¸ÙÔÇÝöï:½–ìÝÛ·É®ÚÉî%ìÇjë&d F¡ZzÞÇ©ëß{ß[Íè5í‡1<|°åïmÞ̓½Ãn×Rؼ#$À»c]8ª£žöô·{CÖ½nèàîÙÓ/'¼â¯}7ÛÝõ½½Î¯¸í×¼ÉuéÖmòì=ïmŸg ®Ÿ@%Û(PíÌÝ«ÜðPã®ðÞ#%6Áçvï\¼÷ƒ¯@]Ÿ@„‹¶Ì†»€#$äqäÕµõ5âÝÀ=±JT(-»¦Âö.@C»=ãw“³ZÇ#)Ï0é7wnvs»9\Ú’H¹vî›d›­ÍïïLÄgS=«).Ó+“Û;Æ=Vû¸|C<ÛíÈk¯£àclÝ::>Œvfê°È–uÒûk;_v÷£4ë˾û©_mƒ»»q¯WƒÞ;ÀѾ5ãœ4Ñh#$€#$&1‚i€™¦™£TýS5Ê#$A¦mCÓj‚S@„ @M ‰&™&4“Ó)©æ¡š“Òz™<¡è&‡¤#$#$#$#$#$#$H‰¢h ¦M?LT÷‘SõO)¼©êyOSGê†hh#$ h#$#$#$$õJ(MM5jmOSÒz!ê@M#$#%#$Ä#$#$#$#$#$#$$ˆÐh#$ õ<4ÄÅ<˜ÐIê=M4Ó@¨Ñ #$#$ 5#$04ÈM©O5)ùLjbŸªfžMQê~¤=‰COP#$#$h#$#$õßæUZüe ý+Q«cN¿…U§e6C>ª­w³Œ†Ø‘X‹Š*$Š#)Ÿ#$ ‚–Š€¾¡0@Š~ƒóÏƵün‹†°Íhüûz³s_ŠO8§§"âf¥T¼'…/1WXÆ#‡ÉüøÌ“´ ZD@ÐPÙJ É70¦E„P†¬92/>Mšcg£#)"îêUŠ‰‰¦²’®fñ‡Å¨ˆš»ÄMÚ—£ˆ©À“#$ÆP¿w/úG:&„#$+iš6™Ý]U¥j±­´–Õ±­±j£jÕíV–Ûk–$!Ë¢À6‰›Â(†0Qlˆ@Ä@H@õÅh€€ª$F@dAPDÈWl2DÆfÆ™šY2!ˆÍ&™¨ˆš‘,dE”£J)¶“aŠ™’h%5MFØÐa¤°S"4M˜±Œ–™Ej5,hLYShÒE&Éh­IK-)M¨€`›- YA0e±hÔ–Á-¥4È&¤d !E4©CJlmVš¶šÚ¤)•C6$ј™!4DV–´f–*JML•-°MšÌš–¦ZK&&IQFÀ†‘²¶B"ÉE2(Ñ"ED†¢”Ö) Ñ°Á ŠCLÆiQjRŒ4DŒÊ0MC"2ICd­$„RÄ#i(d HH†’šVhɉ£aˆ¦RTX ¨aR*d‚,jR˜I‘É M dÄÅIF‘-šRM&$‹DÊjcf@ج–± ,1RHdÄ)±¬šiX`X$˜¨Ø *$ °š ‘(Ñ“JX ‚a(c$¤ YŒ™„Y52¢#P›%D“!5&ÀHD’RV @A‹,Ì6Œi¢`‚e‰")Fl% 3l³YKeŒd,i¦hĦÅ%’6(•!3 Œe, ©f@±%ˆÒ¤–™AF“4ŠŠ¦ˆš¨F ”%‘ˆÙ)2&‚i™Q£0£YH ÆÁ°D2dÂfPR1 š’„Å#$l“*fkLcl¦Ó$!™©ˆR ÖRÓ#)É„’$¤’Å“fÈÚfb&dŒ”²&ͦ#%±")™(lÉ¥2BÆb̦ˆ©D”Y©d¤Ê) #&$È¥dÑdŒSe  E+)$Ä’e5&Æ‘#%#%#@„cBIY¤Ãh4lf2I„²e"2ÄK6JdRˆ„´„Õ6lZ+Bd’’$-E°Z ›FE# la Â2Š’*,¢Q‚´¤ÌQ¬f†„™#)J)”±FDJaŠYcL°"Q”e!¢jÔÛV‰$É¢Œ)‘¢ŠI™­ÉlJY£LRde3K*Y£¦hØ[$¢š"±”Œ#%!M-~Ûk£!²´„„©De±¶6ŠŒTl™2ËÒM,´FÂ6I¨ÚÈŒ-2b6S*HÖDS Í3Y5b$”ÖJBÑ‚”IbTi…ƒIŠZS&LÙ6 U¦6PŠÙR*•JÌ)³c1”µ±cdF¶M2²%Ifmd«-”¥4Ãk"…2#b´fÒÄ–’™M±mƒk cZ‰*#)6Ú%Tm6M´M!T`¨,U‹bÃ6l&Ù0¢*¤)A(¤ECR‰¦ÔÚ#lT‰XŤ,›i$ÛVËm¦CbÂBe2¦¬šfÈÄ4´Š)"ªšÒ¨Å2ÓK3i¬Ò›TÓ‹R¡”I6ÒÍRÈ¢ˆ[BKfL’˦Y5¥©d-f˜ˆ ˆeF’2Å01 H† Ð˜ÀËQjIQ†R¨¬“M&M%€,R)fˆ¢¤–FD1”I#)”l¦hÐH’š#%‹2B"–š’ȶ4Í4"i!€ŠŒ˜ÃL$d´X! !&ÁBlQ¢M“4³µ2³¢“a2¤¢£bÅ•A)‹3I%”(¤1fmÍ£c‹hF‚ɳB’ÑCHTd2#F2š$¢–RЙ6Œ ²‹$–!ѵFšÑš#)†Š–Y6ci3idÚ6Æ#!’@˜È,’XJ’£4Š”Ó £-’ScPm˜lÒm¤¢(ÈiFM¦V³”ÓS4‘ P"DA*RŒ hÛ41F€k4›%1 LD¤¤„¢ÑªQ¨Ôh£lFÆe°Òa¤’… $‹E(‚6(°&ÐVYdÙ) Ø´d,”³If´l$k¨“E –Z‚¢Á’ØØ(ðÆ6ÊPÖ‹A¥)–B£D‘TÈÔDe6ÖELmŒ˜ &ˆ¦e S)ID•,J"Kjƒj#),”Y*5S3E²RV6µ)ŠJ6Sl†²X´4K6d• 4˜¤£$F#)É€Ôd™Ù“)²R›3bb)TU•“Y"3I5£)ªKbÙI4Y QI&V¥”Õ)ª$Ôj¢¬e1i5² Q¬j(±H¥3#)dfDÆ•"(6fÌÖˆ±¶MÑ©*i­¡•b¨Ô…#%h¦k4ÛE£QcV2kRde(²³h•£‹2ÆÔZT°¦ÙFXÔĤƒeFÅ•KclYš“¤ÚÅ&ÙLÍQ£hÃH¨¤¢­¶[m4²&XY„‰41ÒhÑeI6¤ %X­Ø´Í[F5¬ÚMše­ˆ²[SSl…µm¢Q”P26’cÃYS5l²*(ÔI‰$ÊI‘$5“Z,ÍTÇù?Îü—ùÍüç5€ñ÷ëíÿ1+J´4}Ç ·—ýL”“ üpÂ(Ѥ¿–¹¤²ú¯|×óž«×7ó›×«zÄT´ÇþreŠÀ¢hó%qÿ†µÄ#ƒ#) GÈ•, ’H‘0(£#w»KD¸äß‹1ìÒÿÏí­¿ðH’…+r¡+JK ÕÏV^=EŒM‡¹vt3´Õt­˜Hc¶&Û¬-q(ÅË®Ï#)1údop Œj2q!aSM•Ê×DûêäG—dï; _-ñâï‡>µzܳ®¬…ߪêní®MëqóÚæ*ùWËÑ\ÛÑssM¡´$ÐÆÄj×gx(Æ0.¢%ˆ½Û ¾W6Fo[šJ c*è-5­X\r¿õì³üèJH¥¡½ÓÕ[0YB u@©çRÛ*€±}ônð‡T¦\mâب¦¦Ð¤R›ä¬k™"˜Ò»Õ$¶#%Ñ1ˆaöi¦`´5æÎGÝí…h Â#% 2Ãvü ðà¶~†#i†Ë:óí‰/üîÖ0…•¨v.â©ŒøõåáÎt]ÆÍÌ’QlX±Q~9}³E  ʨŠO]Ql÷3ŘVnÒ@)¥Á¡FG8ê‚>ª\‡3k†÷Ö\#)MS.‰mAF#i)&Km±ª4Š4dƒ{œlË ÔÁŸb¬9bÜZ¨¥a7"rpaÆŒ“®éG9¾[Ëοտ¹¼¯*1ií×l}%s¥ßC¯ªo0W좑:5ýN4Ç?ØóÀLð D¯ï¢™³ZbÖ#)€Ž*C#$È JÕ £h§ô`׶~|AÖ 1ýz‡sÒ4¶ÅµCòÉçÚ¯S¿»?Ã˽[iq`m•ù,,¸žWTEÊ5R›n%:¼S“#%†ïãfã¥m¥CXÌu¡ZR#)¯§?ø¼«°³ä›VÜ(äß¾¡[PP±Â0BÂ2'DÓ3n ±j?ºÎ‡§hf$ØM”}h¶åXÿª‹dê˜JJe¡Ýe,#%éÒñôvCÕ¾"[~¼å0’Gá–¡ÆFÐÙŽKñKÑÃË{5Ŷ…ÕEkFÿ­ë£,R,_Ô2Ld—ãë¬Y>/ÓÕäF=Òy¿ c¦Dw¤ßýg÷‘O';Ex”Ö#ub±–õb$ÅqO<Jìiv¦ŠE4Qxæ’Æñ\¹¤YýŸJÀŃàÔF(W±±ŒØ÷~ú™¨(-2„AXW··€ ¤CDI±IóÝs_umtéËrü~u^Kí_sáz±êáƒEËtÛbóÎýôe(ØÖMŸ7rÄO´aFEöT¨²EE|jªJ™­nÅy‹‹&¾Ção¨,–üÎÅÕAhî½F[îÍ ŸÞÔº…$¦˜ú¨[fÇóÔ9ä Â#]+k6CÉ­ê™Uôï×y¹[³ dÚû» åó`ÜiNa+€ÄÐÜdÜEhú±²(–” ´Â˜('m@8ëJn†^®ß%ÊT—Um*íÝV)økÏ!—~ú·w’wvÝÅI<®ÔÙ†¨Ze¤‚0§Xôq«¸y`[&Œ™i.šÂYy¹J©ÛE½”)Ã…Ëb¨‚§>Ú“Í1(Ü ©äû[ò ¡êãˆøFðxŇ¹Ž«)¶åßÌÔ;a¨þóŸFx±¾©áŠ®¤8¿Vq'^ب½­_o—•b©))…#J_šF ýøíLCPqŽœÿªÎ×ßtÑõûhˆhß#)èÃþw²¸3:”… ¤GÅ S“Rߥ}}{­Ýj5(®ˆmìã繊ELbfNw¦Þ|%$¹ÆäFî¨G_½–ÃE‘1½ê RÖ¦·˜ÒÝúndM4ˆópÓò> 0U2#)B­t«$Î$‚TL¢‚E„yêj·¯¹Y€Ù4Ã>ˬ×akÃâ]^üÄÊX綱Œ¿ ˤúò¢VŒóÅÈZ¡=#%˜É0Í^ãÖÈ_•)0ÄCz“ ,>§u@¯×ñ$nÅèÊ£ªL†sœzgnú£êÓ½45ê>—f|=ý6Ó•ºë¢´f©2—$À‡éUܨ¶åTüšL/ÉÉPî¯i&AÕ-)ž5XJN{նМR»• JXŠæîú_üþÜš@C…wÝ3û8Â3ª^ç9õçÉ—"œpã>Ñ[øÉ„=ìë"ÓädMǶf(öȃ^|oH0ïø´Wd1Š’#%¹õöå1¦Å4è=Fj/n{ˆŸ’GÝ—ëµÍ¶%ïÉb½îχ$™Yð«ãÎýé8§~tñÁH"F†'ù«j„ZÖe¼ƒVnœÌBBéùH5$Ï… 4ÑÏrðÏ#)«te¾m ¦ÎUƒ)çu<äíü\Sía®ÓÙïÎ^?«ÒùävBI$Šƒ‰wqÇ] ,P‚S$ÈO~´]¤ä½ê)@Àí1´‚Qƒ}ÚÐQ±uËApX)â.<Ü ^£ƒîñ‰!1ÛàìwÓ±H ¬&€ö7²ÌÔ™ºt­Á"Õš9fŸ'‹nC‰±õ‘8ÐÇŽ-“èü!ø?¯^çxnY̨ö‚Ó»qô“ç.XìãÉ8ÿc¿Õ¿nÿñ¼êuß{ÖŒYTNŒ X§ -L´(¤Rûî!sw½Ôü=u5A'“BÇÛ—æеTµEzUAL¡ ©~|¿Ãyõ{Öö>?·ºó’WÚÔÞ©õ0¤X£eIÝEZ(øPRUê…÷³ÐOÍùók>j×*kV¤F~¶S—ï}ùãôb÷n–Iù"ÛWÙ:n¹ºŽgT¬õ{mr00#%}ß–†{âÝüµWW~¯f#%­fƒY1„ûrÖïPÓ3óÒSþ3°÷iFK*‘A‘èÐŽµ'ÚC#% áÒÑŸ„ÅSSŸ3ƒi4qüÕp3?Œ‘ñleWÞAV;”¿Œ‰æµîÐi ð҇碑B`vë›DQc` Å"ÒhŒê_v¯Ÿ)ù74c皧ٿvp> > *ƒ/u† 1ÆdƒmwŠ1üFvNç®#)oÑvÊcËíšzxõÜtsã±Ä6”$aÁÁif©>‡óD˲Xü]¡2”ô»üž»ïg!ÆJƒu|/¦a”Ë%{q÷™É‘òd¾ú¤x3[…=Û\ÄJÖì‹X>i5;+HÌÄES#)'_ÂP[oZªCÁ6ÈJGïo,r‡ôqÓn!­és9õ»À‘]ˆUTÏË‹ñlÁÍÕ:ãlà ‚W¥ž.!û¯+¶]Ânwhˆóˆ£›#ŸF¨"(˜w³àÂìvôÇNu¦œ¬þíhòì¢#)h]ÚD´ñaK–RŠÄ:3„aaÏ]{|w–Û!\kbb·$„‡ýŠüø¡Ó@]h”!7¤ø'ö9eu1ÉÓ"?žÿéggœá_&Í#)ª(£”´û¤5D<ö,ØI P0Ää‚“¥š:îtTŒ’B-)pàÖ"Ñ­‡cÐOÿQÚôŸÛwsÇ1„bäÎyÖ>5°º?Îáf1ÞY°„ßI[Q[9ˆâ©ˆF òüq÷èrÁBj¯ªîÕõ64âïáÍoUäi b§uú¾}Þnšj¾e­Ï…“?uOÀt ­«ÏÌÀ\Š#=ïzß#)²Áxá‘Té ’BØA‹Çá/.&«s¤fÏÃéÙ1^˜ iwdršòä¬+—onàZ•¼Ê¤㑘)#fõcêÖ¦ àD 2ƒZªHrd™üžrä¥ç]*ªÜuè³´ÃiLŽ7–¼žûdzÂbÑZâÝŽü4úSo"ø!þžÐÆ|¢+‡C÷òâZ« ³?”æ¦;ÌÈ…—{%ø‡'õqWÝÁÃn4A•@¢õÞk4 ¹›9ñ¡E†tͨ·8½ž^®V`{u­ŒU4UnvÔËoÍAŠ"<±ìÁ–~ÿݦØþüQ>Ïë¸,í~Ç Ãù±e‰Êb­â"(3Oéׇ‡N{vçҋó¦»÷dð(¼šÔ¥•TqïÅ“à:€®•\æuð¶ø=|.XC(¸{<m¸!•PÈýT—•óŒàuÆœˆ•XÔ±DÆÔ3Êi±Žëª·Y‘“Ž’‘= §g}Œ|ªª˜ú¡ŸÏTËÑlÕû÷âII•æcðG,Ž‚Û9"}3ð`qcÜQñ“| «LOÒ|Ö=ñ_#†È+2F¹–—ßâ«€Ù7úÊ\]s—Ãì¿-}wå|[ŽÕÕ¸îáÊ?NË®UVzX8Úñtøð³5µœBÙ–z3õ³ÑÎg|O++àî”Ö ¸Úä¶A(ÖI7š5éQ…Íûáµ6ÎÎ5àä‘{zÕ›¼n‰Zïs#)¦µþ:û°yQÚÌ‘Q‹Ÿàé›{ª¢Rêè¬)\]œ¨ød9¹?7MKÁ“M@È•àØœ™û»kfjxëE¿T.õ¿Û{øê[i¦K­`QèAt‹‘ÅîVÚ­B—Ïç‘êXû^~ï~Ï)Û×ÈF\†¼¦JýÑžñÛGYeA,·èšߎÁ-5V%ý5}#C@¿Ru‰p݈—ý) ¸J¿*–ìê#,žPi®ÔŒgë‘þ®#9˜1ï—3)˜õéf#%6•T-_wK EQEž¤ú8 娾ì…yú"½²ßÖÀ¬[ìi7FJ(ÙSôû*LxÑÜìþ|ñKXueØRwg¨vñQfâ.¾j ‚ ãM±sX²O|#)x#$E³[ˆdÇÃySÊïPŸwg^Û²5ÎÉ|Ó¡ÄÚÃœ¿íñÆlm £H˜rBiªêIŒ3Gž^|sÏQï¾G˜m,oD>Z‡“#Y-O˜Ñ?S‹ízï…÷Œè…ŒmUQ9$¿^,d/W¥Ó ÇF¢e¡g½„ nϾNÕÚÜAnèýFÅTý±-ë \íg·.ž!±2Îaú {µ¬ÂØÅ»ƒ—󲼋H÷à£7ê½;ï¨Õ# º»¯…”Ã@Öš»¤YÑ©uÝBQù¬}˜Û׆fTH4˜[òo N?Lµ&° TTM*`½)àÔ‰ÍHŽ#%*uf3‹D—uK­P2‘š’…;­Ñ‰<÷ö¼áWÅñ‚ꔬ©«ž<ÝÛ›Äír)qÕw']a}¤"ªÅ–´Än–ÿGÃÂÏPäËÙ»w2 t•P¥‡gCOaRI;âë"òœöþË•ÑCÒfpþö2l¾§ð?ph#$šsñÚhÓÜ6mË¡r…Ê´uÿ·Ý/ã]îQÁNÅÌ”ú²•ôðáiŸéÖNoxÇë3¯n•x†QSÆPªo½–ë ¢ÞÜÅIV÷TGo(Ê”Æɳ{òk»[aÞwuBC5˜<®±k³}£Nؔƫ7sæ¬ÞêfšJ\ÙBOÔìýz5ÇW#%É#%‘ÝõqK­f:;˜_ =ìû+­ ÊºzÇÛ&ÛºýðÏng³øè䢓J)›Žßù#cý¬¦à}`Ö1|qª›¼2Ý}ŠTÈÝØ#B5ÀP^TÿMNÆõï¡·PÐÆ1ÙT  ZÞ>2ÈÐ þÇÈý›ýùæÿ„0,å ‚Ñ¯OÓ-ùgŒ¨v.>]Ô»Ë+¸Ù©QtB¼Ý¦;+‹žÅyig•;žó~‡ÿ¬Y)¸”gÆwª¸Îÿ\ ÜÎ¥u|ž¾Ì„šv³´‡É÷½¡ªÈ÷}Ó;XìÖîJj9wmìpû~.ڻ㠙¤N“¾wÔ÷x<§;'Î&wN—| Izç#%ùv[ê…E‘[Ç(–JÞÑ#•97µP8dæ•Èj"ºÖÿâ¾™–q¥Ã›~Üb§·$‘ô[@ÀoÏ0Û[hI†Úªø¦å¥ÔŸvãâcE`|ð?¢„œŒž!½¾~&:4†ŠqÚ¾.4L§b½Œ3·º÷ÆlJÁI­ÈN¢ÿãTgßåÍ9i–Ÿ>nI7Šèò›»¼»¢.8“mSZ–ï´Cˆ|ü|~°Ñ‰±È°Õeê¨Ãèh¹Ð4•Ó“ÙØK7(]…nêF!§ßżèÚC!¬¸'|%Ž3Ú6[+ŒaF¦©CÞø ¥Æ\¦ÛO‹\m‰”D¬Ýdg¦š,@™Å^@ÁØ × XkZqÂØÝ79v”¤^õ#$#È@ Qä^ jž½êŠ%©²¥ãm.ºÕž³e¸¾Ýu´Wƒ r¨@Gr´—ŠDŒØin¸_¨f!b®Ü[fÜÙ²RqÊå°\#•‚åa¶¨Ž'ªôßwWÈÏ;ôð®|øt³7åKýÐ'€´^ßÃ×°Þ¶“³¦qÖ–ðJío,×±[”Êí. =µógpaýH­}>!×ó±1ÅG“é-ÿ.üñÌÛU5žM˜Ûïî¾#)|ð$RFÂ/þš:¼²°LsH!üCo:“C™‡D†îOõù7jŸ%Ĺ²L`ÔÄkm(0@§DÑ@c0éo±â‚wÇ¿2òFnž°=\j×ÎØÎÒûqø±œ/]_O¦Û+çì™ÒS˜ÿ€XZíb•åGÞäpA=ºŽ¯¤5¸J8ù•gˆ…áø—o>öë‰W›Ç~{vÆ|Îؘ|}XÞé›9N¨C?W=yjÈËEwˆ-ƒ€±$6¿Ik¯jôn) ?bÇ*dªp;ÇX^îòÉ[ǤÒ'jˆD·x=Ñ­ª*,|iž?L$HyļQXþ/·)t;s¹¨ùc7ãÇ”¬Á¢Jiä8C@ÛNlŠ~G‘\ZnJmG5Vê]ά‹©“#$¥¾fƒN†²½yÔbç 9'Á¯"Q¹Êýa`o¹Rª†â!-LÁÄ|‘T¶ñ´D;ŸDYßëZË;™Á¥¥î·»#)#$–‘·z&iÃíÓë»—f]¤f4 R”þÎwzzÿÆnQ¡³íÿ6íßÆòçB&ôÌ/;•ì?PÈcÌ–¨ÍR‡z¢òÉ Lªö‚7Í»ówü¸íñ#$Mwþ@ÙìK‡ÍHï÷ D @HƒÐKüáOG 6)Æ0]¾Iç¢iJ‹$ˆŠB´ŠÇ]¬0óp4GÀϚšÉ;n-Ê|ù»üÿ-#“š±¡#$?‰Q#%[Õz<Ü𢟳6vã3`u`ƒR¨%â0§`gkžÓöät#$&Uš†È„‰D( #$'Ëû«ÝöT+£RÚéÒçG¨êÓ(HzƧ`>(`óÓÜÚe#$7ú|güRpFäŽcÄy èRuCöÍ—0ÿ¯» †w*µÕåD̽žuD2#)ŠAù*†ÀÉ&~íêé1[™p’»I´ˆ€òsF¤Ž§ßƲj³Ñ¯¢w|‘jG®m!)±„gοw×Íi(Œ2óøhêN>ñÎvq­ Á8XAEçÙÉèbG—Ç_‡ k’0K}´ªñúÃPˆ­`‘Š#)ž—1!ç@ŸÅ9À"&~X<@Sàä…“Ïé·Õ*c*&ûþ0û`€v€Á¤&Bmv>íÂíôwçõrÃJÃÛ^Õ2Åaðžhk_Ãú§íùÓ Òµþ’àØ6ì×?¦X>á24",Œ‘Ï}¤r$nZ9hNz?Lî›+e÷ég¯ÁâÝ”×xþ«øwØjS@¹™aª”ªç^EÈC6QŸåk]߀Ŭælôhå}¹g§MSøÇëáf²2|ü5?…‰Óùy\á(=cŠo.z˜:ë©1g›ëûõ¯Íïyiœ=›ð`>#%9å•Èe5­~¨úº¶¯"ˆ@ñŠ'àaŠBÉ’Ðì€ãêÑK¦$´! @`£‹”±ˆ|C‡ûWeȽED=nª©ýKnŠ{Ù(Á´É%š¸yŠ²7|IàÇ÷qþC9UWÌ<µ N×VM…ï6ÅÉQ$QU@›yhŒéʺ¡¿º‡fžÊ½ä‡å½’&ÊëϵŠ6ØŒž<˜à¿lûœá™¼“J°ŒéôZþ#ÏÞþìiq¦´-8ûǬrÝ“[c|JÉ*ÚILw]ê¡#ô}#?ÑÇMu†¤‘µwžç´Q {d]=K±þ¯ë¹hQl""^ûìŒa'ëïãîW3 Ÿ/w«3"ðªAGHQ:Üþ=˘Iéïy'É0&½i³¸påm­`zl6}b©Œý³Æ,-˜}t÷y]¤_õaôÕðèÐÕ†#)g…â~dÿ•¦Ã¨úBbb/˜ÚØ ¤­Ï›µé·ó¼žäi°Œ4öçëÁ½˜GyÈ@o³ÑÇ«„0­¡‡û²AMObô(ñm9N.Ö5Niݬçsc‡ïÌ“—úo[´ôúœÂ½ÌÏ¿—@~÷LJ&hŽ”Ñ3òÓ;³Ì#)JK6åô]ô°£7›²¨y_nP[L§ Ã0ÏXI#%æ°(“ÊTŽ‚ò#$KšwV~byþýú__>ò·”ÿ„<(Ù ¥¢y¢™ [ˈïÅÝ;´Ã`D!õŸ§ßuy&?;¼y›sŠoz`×ñ)Ô°;²ïrÃI}x—²ð¯uñ;ÿ^ù#%#%®š¥U4Ñ­#u-QF±•OX}ãñÞ–xí)ä0ëÊqZŠ÷û–˜A;â#PßC"S …o|d‹c±¼´ÞY$j±©4mÓPä¡Eù¾¯sp¾ÅqMYaÄìÙÌ;ÞÇŽÑüšÚ‡ºD’n;{ûnSÀ‘Ê;Õv’…¸ï l­ÇM#°ëù—Æ<}€zh5…ðþ¨q›Jõ;[I¬Cc§|}x‡UFütãé¦íé~Ÿ9#)9]Á´™„ Ï·_T¼‹…c§ñ¾Ü+kèmi¥¾n@±ÙÉÒ‚LU°íî¸õ¬CÔgüqÎ&öw¨vçz5ˆêd!¤MÉœp°•¾~#9ç#)"hÈö )¨Íãhqd.å1!˜·RÆÙÃ|ZÈ*ÄCW—¹ñ¼½#Þ¤Åo}žžg#%(c“þÑÆ®gœÏµÔhwØ¿â– L¸ÇeÎlEw‰*1"("*ÊM™ô7ƸޱØb²#)lÛãüá#$cTÃÙГ^ÝÔ vx§RÇýÄs‘V*-æp#%*Àºìa7£Ê!¥^Æl¯*Q0 "Ô4Ø}Ÿ¿Sê=ú×ÃàR³Ü÷ê¼Mõ×ß} éEO £µ¨!Äsü+ªyÐAû–µhˆéY¨Ò’yv|ŽÚÝÂþŸ–ĘÙϳ‚:ø ‡c¥›u]ˆe7g}&Õ¸…î^¬vÑœ_®"#%œýÖºs#%Êι}Ùi‘©¯ã½ƒh^ßÂìjmu¤PP;ÅGY¬‰—Ш ¡ö²Œ™cXV¾J’(‹0V“‹-ÎRprÁY²²&j³F²*)™5$.ÜÌëwéd2óÈX㊅Bå|ŸÂ8=Ö ³N®Î‘÷^æ-tu ŒâÛÊ5Š#%¸ég³Ã¾p˜|#ƒNF"ç/P”ÃòÖ±(z¬¸Õ>:¾—#)Ÿ•ð‰Ëøë±ìùÎÇZõ¾ŒnÏ7!#ˆÇ.ŽŒ;•±·£G;ksc³änžž~çöÁÝ-ºâm2Mä½Ý\*OLU—6ú²‹qÅa¦¨ˆòê †v²#$Ê#%š$À uxê"’ÕužòŸàCè–}ó9©Ï¹àÏCû¯ãKM½„.ÿ*ÒÕ}¿cÓ{9ö(÷åù’îAþh5³Ý}IŒœqalá|QduD+661Q\WWwž.מ9ˆ±ÍÑi’’ "‚Ú;_—¦uÓQ½Ÿ†õž¼ŸvpøéÌãðy!d ¤¦EU#%…"²ÌUa˜ cQ:Ɔ-zá‹oØTÈÆ cŒŒ0Hl±NÄ2p°jîŒØ§§>YÛm*vmše©U7`³JoÍJ}*28ô”RrIû¦tXìv/j¨£î—`ܪ#e§f†hMXþRIB⃱‡0,®Pã{W”t#%Ö77m—”Ýß©²îÇ(uôåêXubM„`NÍkû¥ –@¦È‹“’€`V™)¼ôå¤ÑmÇ1 ðaÝ!aHŸ#ÊãϵÆLëm_ ²>aŒšEoõI½ªquÛöˆý}',»3†y_ÑdîÞyÈÓµs½ôÖÌ™¯>ße«8~±…ZFçD·`¡<}4DqKXÞ§÷jbhÐãA¬ÃRbeç}"hC’æë6Ç[1KC”›Šb¦¡H ¥äe8ÆìU@SFäÃÃ3G|ì×C´.Ó7_gžµê¼[•+žÑ þ xÌ#$CU¡T«R4X^ØlT£ x”`aBÇ—£œw÷ýo¢?M¯]ùó Hl’R±H¢6… /QjûÊóç­¹¬S"ŒT©¨«à›nhÛzÔ[è©çzÑ“%,HŒFØ£U†6Ä@‚°ˆ²+–ÊÎû«.·›#nwØ-³ÛÑÙ´ÇEÆ>³{§aûŸfÏŒsF·^ËìÎ}71ÄŠŠûVon‰—á×î¯gœõÙ½KõzfmÁÜè}c¶iPÁ;EÜùû“ó£UÚ“^º@ÒáúEŽxÐ=Ú-„#%+Û· ‡ë·©¢ü”‰:»—%˜Èfùf<]åæ¶ãVC»I:~ü,Èû°+>ò¦Õ芘s=¶¶ƒ Ë–f62±WS+DVFÆ”–ƒrA»k&#%}+ž_¦%Ãk&܉‘¾Ì‘®ÕF4TàÚÆûr-nøßžÞÉÙ@>­SôV¸ÆÅéA³ê8z-Åj l!³ù`3XøíãË”±õ¾«ŒçàûáÎÄJ¦_sÞª˜ÂtÔËÉ8ÙïµÔŒajO#%@Ô™¶¥„ÎÏIü,ç#—úú¼tÛ#%ý²ïç£÷_À_¯CYžó) AÄ×GKh¸Ù¦’•ÌP"é“ŒgðÓÝ-Ƶ ¶×iDzÕA¦æƒ Ð^'v7‘³æ¹1‡c'¦ þ!Î?¯L<1ºmË1ç³ÏWëW)ñ²²Öˆö6¨Œ#$Å<ÑöšØbR"¢h³º@k,± nu¡\ÁéÅ*¥r%RRkŠMßÙW §Íâ¥Ø7>×Úòø^”*â÷Y°Á(&B"¼ó<9«"+ ç+ÍÌA¢¦”Ò0Y% ‹24¥!¡8°‹|:÷ryvëoŸMQ»ly7°Û#%<âA7ŠÀ±œ2A¢„Ò¨o.¸…Mh©4iX,4·AšM¦hyš £MF¦¬(iã0Ñ ÄØ!ˆbÓK³#%ý{Îú '„DbšCx2¹Ô°Pâ²V9£ñQIŸn5U念ƾòé‰\£Hñ9¢]§—ÉøMƒ¢ÍfîîŒï å#j¹Ë}zÄ®0äN¤JQ0.ˆò¸á§#)f§ð#Æ©”cø}¯T̆ìw‰ÜL†ëd²Ù#%\×+6 6+²êÍflöfþÝü¡ƒf; M;Ã&òcÑÊo9Q¯àn@°mN'<à ÷wçפùâÍ"Ê#%_SF3Z«¤Ž­§ÂÿmËÁÓ %&DüªªSPÉMسšÞ)61‡ÊDÔÛø}æ#)6^ÒéSäÃ,lûEíë«5Çìñ“ñgÊq­ß2¥Aüí+/÷;k¿\~&Ã1¼m5ÉÉâ3‹³eäÿÍŸ«· 2c@˜4ó&ÃAN5“ÞàØÍ› Oð·rg¸?Zùuf/œçw#$ý„kª’m3¨ÊBvRRkµÒêQR®%!©íõ^RôÙöoα¶éÈÄ㓦4±Æ²¥$@-ís‰~#æ»­½Ë€>îÍx3›µ–(Â&†’èÍ·.L«˜Óca5"2i¿OI_\*·÷‹˜q}/l°Ñ#)¨Dë‚sÙˆ7ôk·Ÿ ÏR%O>FÂU=Ø£DüqïÀ*l?óæ#%¢ r/y®vïø¹ß÷r=¶*±‰ýUm´ˆªT2«>ՆبKñÌåœÑK«¡`Iµ®­\Žh!ÕÚ÷-!°çS&Hô ºX€¢%FÜ«\©"×6æ¨Öæ’ Á#% $Cç*LDŒ1ƒ²Ù¼» A w ó¢D˜0‰C%ŸÍš“§ç3kP q 'O¼.R†¯ ^ŸÂÇì;¦ÙˆÔ:=AIë=g¤&^`ûP€žÿ_QðUŒ3–ƒ°ï̼9Wxð-¶ßFà…òTâ¾·®Ÿ[W¶äã@Ì+Ò¥¯¨pÉ9j 1œÌ+èöéýÚGä4õîÿfÞ^„lTbƒç)YO,ÏTǤëÙ7 —|5y?-·ûµŒ¶Æ‡?žçr?C>€fú'°`À«eŒ@k(ã|‰ÁT>÷ŒLþY….?²­æ3ð?Ã^jþ[Æ[]¥)WbR#)²¦VÔ'è¤L¥Â't¶t¾¸wÖ­»·ç#%—JÀ¤rè^’ê ÙôC‡GÃ`È$[…°ö#%; îꦙ% ÛªéåáeŸ};9y_ô뮎nÌ>z#$×ã^hk>50ïø¶êÖŸ_Û«ÑUZî®#%o‡<XDa„F³×¡tÖ1&.HGk$‘å<›jyø.RxšY`ý(²¸ëô|׈#)Rj e™sHsRKŠ0 ³@Æ9‡þh‹ÆAõ{>ünú¿œ×·Ô`ÓöIAÅa\óÏj¯%â–Û³Š¶£a–6Ý°¶#ù?³¯ê#$Ú(æ#$KÚïãjÂ5&ŸWëü½¹ø€&zê$I#_±Ë]ßλÁöi[=‰Õ£_0QÀD`G”¦×r~‰“Ûb,*[Ÿµ ŠfÔ–OÄ®dÄl©bQ~‡‰£MŠ¿¤_mÚŠ0Áª”SDB¢ZHñ÷;b[Zþ€ý&ËwúÍÛ0öÿ1Ô?À"ôÙç#%¦ÚžVÐœ tì•"»#$þÄ,L ³î¾¶hÅlX¢”U@ÐA¨>AjHvÕå¤'g»ã_#%ߎ'ª{e`¢Ê%£AÉãþ*”’‡Ï§ßýKÌí¬#”J8äblÁH)ø¥0?Üa$¦ïp7ùéMh”Í2 »À¦rdZbÔ›^M¸†Ûʶ¾¬ª¼š#$b`ªª¨ˆ’´aùfãür}é‡Òí©WU®“)Œ"#%´¡Ø¤ ñ_à>Èé•þ¥mpQ¨+™/oîrWKCðµ`ùFY”þýX±|°=gö´º˜`ë ‚ f˜fJ®“ïÇúç«ì4…Ú³Q3ò¯Ð¾ÔÊ礞;Îãq´„ñTr…@JE\`¤)³Qå]¡¸€È.²wGUbw: Ý¥‚Ðr!&˜_Q?µÃCAêñӗι<_‚>(œ¨Pˆ# ì¹4Þ°È¢¡Q,;x…#ô—P=|î˜5_¤ÃðhÌöýìYC (ôÚ¡ù˜~-¤7vtªÂB‰ýU&‚ b$þ¦ÐøïÓüû¹h ýVRßì‡ñ#½qÝæý“lýÞߌütþß‹¿¤cçŸX>ï“Þ ÿ|9]äÜŽ…Ü *8yòÞ3F¿Ü>šö²C%^ ôð<Žú—7Ž0 ;ˆùcIý§?¼`‰šSH±ªÉpÞaº—}²W‡ßWG#$.œ[…CÊ.ȃ]‹ƒ­oÍ_²‰Ó8O}Áˆàdó5¯·*׿÷AFøéåãY­+BÇ]Gãö‰å_Ó»~ý‹ë+þ=Ý‘Ù[i’wåéýrPi¯{…9˜ovθ$l§tFq¸›J©Üsªz¾;.H~ª¢ø1¦ wP]·©{Ô1Å”¿³­,>*4ÖßÓ #%‡-à@zô£‚½Ê÷bè6Z‚HRÞ­úNˆ»'9Ürw¯šºÆ–çqðp.Q£t™-¿ê3!Ö…!USܯ Ex4ËŒ]°ˆ•”ùˆýiXœHú@òœ†€÷ë{ÊnG#)ßB0T ïg#$\…ãƒj?Cg¡ã¨P2šD0*¥ëœð˜dÅÊ…àH*å,ê1Ð ‚“«;Ä…¥2˜it.0TV7E3'þÝÈœËU½¬©#$à„¿~ùv¤9±wÓëï®#ì±£ó8ÙöîÓõ@ý–u$}Kiðd ‰8)\GíêQñÓ@s”ÜÃœVA„7-Ä—"ÐB¼¨$ ÒGòÅF5|û*û&伄¿¬„$PÖIq.Î#ÍÙ‹jF• ú]ðgû?ÏüÕlÚ&;Àuùè{±ëLabýkÒëßGuÒ„ItV+?;´išh€Å ÂCc뙧¯Í ôÈu5´XA"µqÖsÊE€¬köÞÀU(&òÀ1#)Jó« pˆt± 4Ö<¯aüî&Ñ›²òIÕ#$VP”#)ü•<Ê.ÍËWTó«•¼i×X’¹pK®r;møÒƒ÷¢!T›ä.#$-?„…Ggˆ(,Ö¼ác#)K@¡’˜©·¢a"ˤ Þ)#$ˆæWÛ•eŽ(ÏCžˆQÎDAB2ÝÌ(‡k3.9ôÛü¹©ÕÖçý]óy>‹<~Ž]xr@ó õOßÇ5?>‘ó~ÊižýõxÛöÕï…”ùòÈ.x²ê¤gð¤h)mU×~3éoWˆsLIÝ‹èéüviÝn›ZÈãn\ÿd:ñìÓîžývMåyaå»nl$¢‹*{~ŸÏ„0ËQo!òêëá/ë×t2ªgSÃ~¼Þ9ÜšÝvÙtx«N—òÈSsŸòݺ_%ß³W†>SH¬}ׇ s/.Û>hÜàÙYp[…@MÝ>zßO-¸ù1²c«Ž^;­ÁãËË™(ýÉéþTzû¤ØB£Žsô|®Â#):³»›–üÙ¥F³U¼GÐôÛÇÒñÁMR¢9©ãÌäï¦.×W"ÝÍGNÙ6~L: ëÿÛ²w¹:gŸÊ鶵Œ¬®ðüto›ƒ¹þ¯§MNßÍzYËü¥QvSÍðpÊ%Ñß›>0¶—Ð2Ž‡þÜ€Ôu#?”Rî¸ÎeÚ,ålc—A”])l·~9- ×_mT袺ú…V¨ºêèaMõKMTÐ#:–#%™QŒf=ÁEbÇ<Q–±¹„\®ÔàÐy‘sÊ®j>¿C“/UXæ¡vøïÛnš{w×ݧW”èÐMr»O¹þQêô룯E·uÎŽMí†ÍYšÊrÓUoæXÈmß:òòÕ.^líI€ãßΧt•ˆ‘™ü7*¿ÜêŒuûó3ÆâF[åÈýQèù:ÅŽº9/¯ïx×ó„Î<¾>~UêûÍßÄ{ƒ ±ßlèˆù!¸êÎ1ˆ»«L*öÂЪù«¿#)oúqòf¶¦ëÈSG‹«Šº¥ðY˜|Ϙ`~½rè½™p¯ýCQ«ì•¢/wöýuë›MöÇž(—õ»Ô9°¤ý´÷ëªêqýTDŠhN6¯»àÙlÓîÿËùfÀz®Ž³w§ù{q‰>Z³h0Êûht ˜WrÍ´×/ÃÔÛòFÏÑñ8P3Ý\3w¬7{8v¯%ëaÅ À©'X!y-îý¾F´Q›­Ëè©ž=»ÚìÍüpiúøÈFš$.WõòúÙßÒk“€+û#)ï÷iÀÿ·ŸoߎIH·‹"¡Gu¢Ø{/¶\šç®uü^Ÿç:~}c“}öþ'ßòü¿S~÷æìËMVòC#$á7aéóÑ»Ç3}ÿ˜!’¡WSx5-ååîaëü?©‘>øÐØ’×¢‰TtvQWOâ>°µÃ{÷H¤»ægr@¨0¾!ú¾/c@È}o(>T‰Q@¢£ BZoe_¿@_Üóæèêç^ÜßG§m›yÏÇæöy;9ñèx,ͧîç6á,ó~s­>®wí`D¨£‘¯Ï›GÃ+?oÒi!sbÿ©zu9yéaúìä^Š9¿,Û…¿?G zGà6ŽaáÃÉÜ?xP·jº>#$;ïÏ¥#$QúŸÊä“ú‡hö~什ùÿoá3Çá+œ1¹56ºý´ …7‘' ÿßk…Nªøò-cÓ¿w°ù?M9'°~9„Ç_êññ fP>üÿ'Ví{@ø‡Ýô©::÷0EÞ¬ð£ÞW¡ì8)ùÈfh$54€ü¨B ¾ˆ#);^º:i#%B$ÂAPoæœu¼^2±ôôÿ¾n±´[«‡ÉúHñÛ/ab¿cpv#$÷×ÕÅsr;÷Qºš ’ûÏeÏÉB(÷‚;¿_Êê¢&H;“¢ø–ü×yhÙòé[º8#ó¿f#Cé[œOÓŒÄÖY#$(ÇaPœd%„Õ#ê`Â5e.CŸr’fH&Ú\Mw y+¸9© °T ñJ$s•#)Õ½uWœò_·<gLkˆoõØ`ìqÊ®[©ÝhÜeÔ¥Hˆç`Àg}:£pƒÔŽ}ß·t’º¨ša¾—š¶.Ç\E¢^š I,1Ç(Ö!œ „> «îÛŸW}¿·¤&¡ôêû…¾½ÝYã: ˜ër£S¶W=% TÊŽW¹ðœÃõÑ$¥G²‡lÑÜ8Ãm‘“k—ÕŽË;Þù½Ík/0ŒáówdÊâ´oay##)ê£)ËÇ÷æ¦{ÄtŽ‰è±ÒŽ!‚¹óË%ûÚEïisÂ<¢á‘¯;éK2yÐówz• DBIÑU^г«P$ý^ŠÌu¤ªÍ#%1ÊáEó¯#$Ú2A†Z»5šæ1÷].×pËËu=o½¢o<{ë·¦8þZüGbòí¤5úûþ ?$z‡"+Kˆ…Ä®‡Š í¢ ŽÕ%‰˜#b,7ù÷gå²#$‚´a量q¶ý§×LÍÜ¢iêÅã·FUB#%ÕT颔à#)}ìö}<Þõ9Ä*<÷Ç‘-‡Ã7K® _‚(çÏœ#)nÑ«ŽzzŒlž™ÿg[Ñv·/ÃëiN8t»~̪—E.‰äÄ™3?¨°=rØÍ®ÿ+âÙ½Ÿ6àãì¶h仳ÄimKå¼\Ç´g?‡›Kiaàw»Íü5®g¼¯ã¿ÐÏ÷ÝðÀj³…»<öøÆ5ôÖ¦IBitàÿeè}®ëƒ%XêÂ/u5:ºÝÇïtéô]l»?<Ô <âðÝ€(Ò5–写Ž$’_¾áv“øyæ*¯åü•%<s ˆíón»#$êDÉ*1ï—ËçÌŠL€§Œ4ÖØ#%%ñÐå7våóeù~Ÿ8#X#ÌÉó£ÜÅJŠÇÊç {ÞÎr–VÜ„¬g³,qz¤_™ã[.ê1í­Ñ†¶±’Â8‚= ©EJ$ݱ!±Ã"ÂAUZPq¸TEŽš•BÅfŽîSFe™ªŽ#)˜*KhfLvÁ¤Øi’hÀd,‰¡¡º¤ŽÆƒMIc+ŽF1Àvéš4ÒJTD0))'φq8&¦dÔfˆHÛì‹B{qZ8dÚu¡ýß²ø[Ñã!xµœ¡AAóýÜÆy,?ˆ—”b—ñþ¡­G%¨½tr§D,;Œ,"Œm&;Ž˜xrE+ðøgOoóÇÑdÈ#psuÇ…¤ËGGJf³v…¤Þ ÆA±aR4š„!¬÷â Òm7&²2¨þ¥@% IšN?²ôöË]žü}×W.Ñ>œ¬çû-莃eŒüøÝ;1ŠE\+QÊj«6hW*0åXnÓK#%ߤã³0üú¼·ˆö"®Ï¯ö÷aŽAùÝ»n¤t=5#%'7ãœYtFl]&®tüÕ˜ÅÎóõ,©ª“‘º©ÛæñÇ?êüÔ=ó”auRhô‰$v»æ.ïµ*Xíäó~ï½}òñ͟Ø¢V„y#)ýnç#$2#)4ª×õm`+¬çøq¿ê…[vrsã[—iʹNΧ{‘ÅÎs=ÊêÕ†¼½VÃøôvǺ?_çšGlƒº¿½pOlöÌóùø|_df_O7÷×Ë×É.ºþ´N‹jþ}™¼ô˜!âü#8°:ýåö«Šb~ž°vM'!Û ü¾Pg¿V„2biºÑäjÑ"¶”ÁÊØ¥;䧥‚£Z!¡”}íæ¼^èóÏ9·Œk»µÊT’"‚!R¶¸aa‰F$7d™eÇ Š" Û£Ó,E…®¡…yjÆaYlïí¨ÐòÓ•·­Q¨È)j•ª!l¨Z7UPÒ؈ÉAºv“y»žL‘wä%r(*`¥2 ÜÁ”ilÄA4‘ƒ‡}^uWm’šÆ1Ò!³,ȘaÆaYÚ¨´‡Ä§qÑÑe-®g´8o™Ã!kAeBŒj0€0®ñD³O†ökQF†Æ&,‘&T8Tâ$z°1ø¨­Ù¤È#ûš#)–´ª¬šŠ¹£Pgj•DaWzeF8×Y t! Ô ¥\3Hf Ü|ªÃU¦jJÙ4Ryµæ›·œáëP¾i¼ßCÈöð,j©ú¾?î:¸8N‘ö2ÎqÕ£ùgGeîù¾Dê=#že ãš®[ý¶„öûþ†»Þ^ëpˆV©…qáý”>û3"°ø?½®ŸØ7ù´ripú0š Áë”SÈ1H#ÆõèdÚs”R¤E“¼Òw,ƒžÏ+Îgáy«_œ¾rËÈÄDQ率½ZYèá/>ª3xú1îpT²¿;œHW_ü,_£LC¢#))ˆ§‹”#%áyHâHr3?aN² q;¡ùêçÑÆ…"[ ÝFšCžFÔЃ%øºË™rPUI$³*ªµ}*>¨$uXK€rT‘©ÃÇgX€p‡qÜnCôÿ…Üùƒ™÷üx”„ÂÝ#Ð$no$ãºÛùôáƒçïý^¹&'UUP!ÿ¥›7wN!ÞÜøúgÛô×ËÜk}·P];ŒÑÞ\çxÈ}¡¹z;:™¶^/ônKaŽ×¦•;üf’&‘h¯°SHÚ>=ûßm+Ñ#$Îêà@°PÜüò¯ãøÔ.%e2Ð?hÇPª‹X¿ßŒ6ŒVYJ%QFH‹ÔK÷±[#$Ÿé˦ðNpèìJ€‚¢²±D`ÔÕåŸgóù0öù2¯×Ê9o[PnÀK~äÓóèh>•µ¤(`*‘b0Dg¬×6úÄ•Z,D“ñqðà±…)“ C-6ÑÁ­ÚkC+E5‚àf-jkta–4A„By©Y¦=Zê÷QVƒ!Zƒ#EÇ3ƒˆ\˜e¡A¤T`ÄÊ+Q`|ïCÑÅ¥â^¸˜X‡ªk1äqVÕ£¸ÖaÕ#UŽ¾®CfÇ&ØV5hnþr2¢0Õ‚åS¹•1é°2ï2ðlˆ«DÎÏ€fd²À]€æš¨¯oVŸ\!­VÜcmÄ2ÓS¶¡Ó†Æ¤ÅöV.Ëc4ÓhÄpÊõ;Ò­£G‚í3»;Ct8Lm‰ÖÐë¶X6 ‡Šn¬ÈdëÁœ4¡TqêŸÇþ/ôìÙ°å¬3å+¦û 0×®ö\.»¡}÷{Ñw­ Q'¶AájÛóº)⺙p<¹àÂ)X,5¹-6åù~ÍOÛúŠ›l hNN~¿ÅÂüÝ€v7}ñŽ–š‚HÍ™( #$äTbP/·)“M‡î¶ýñÒSL+_ÈrAƒ<cžÿñåãB`FÁôîŸ!ú1µ‚ášsO?#%¨÷äÔ7¨ŸA½:¥VkôkäÕçÀs§8'œ°@ëpZYž #%ÁèââåAe`mŠ°ÅƒSø¡UD q¤{.ðÇ·YŸZ5èÀÀâG¢.×^ÎDTêvº‘rŒ‰7Sk0w뱪ޒ'h¨6ušÃñw†{wyìbÚ°ñÖ`Ó{x9 GcÈE¢—HZ-ÒÑñ¨ÁÐ܈¶ÝÌ#%Îm]*«EP²§-¼ÿuÖí ¬dôq!å&€P””f4½ C#$^~mÿú¼þç§ÙËùíá)O5Ô»—¯!,ØuŽÄ÷^;<?¿ºŠzi`CàŠqU÷aø°Ðñeã‰4.ZP)‹uF67eÈÇs„Êæ ² NŒ)‹ƒ!oóÈlj00Ç;1âê˜Hyƒ#Ü$mêª2©Ô";-Â|c®áÞË~åÝyÛÝyst3‘hÇ#N/J·^Ê,°4'×æwÙ˜q¤lÑ ÉÅ›pzE°Ì)Q’f’™PƒA­0ãÅ+#$hg]´`ž<f–j†‹´‚¬-Ý#%þf‘•s+|`ª—ÈÚUèwHn yv#%Ë«w•#£¤ƒ‡ ŒÀÓ¨1ŒÛOµ]Ú |7"'kÓKF¡§­4ßGŒ#%=‘ŒžÑ9N-î"±–Å`j0tª<ŠL²F4GL„¢“V¿²kYýÖEºSÙÉ*“ÆÁDE*Œ;˜~W䥾hÌ÷4Ýív«ÎbmÓ|‡…¶X[†xÒ­A7íGãúý¼S»a¹Ù_ÀœP^/§KϦ’¨J8Ÿqc‰”xù ÁÒÎäñhv-ªÞÂëŽÂ+ûZx†ßY«oMFä™L[Á#óµqg#$:á3m¶ƒçboÈF–L¸G’/s¸3w1–ÛsÀËʨEFj®:çdÍ+TºK.sKqŒz±µZ$ÂÕ¸d‰¶õªÚÎ…ÝšY^ئfÍ{—†Ýô^=õx°v Ã{๠‹ã³K°ˆÒ†‘ή“bKøV|Õ^Ú¡›Û†·uú[I÷r8ê›Y«#)P<N=¦,Ü9v‘$aö5£Š¨2áBµÝ&ÙØÜFÚäØv<žD?mÓƒc3š2Öä…Dx¹Ðpk‚ø¿+ÞÃ|ùðãZ’O:u=\¹Ö,í¦píœâ7ŒÞ×OŽ\ãÒ,aÁ¨!µSyŸ£°q2!Cng¨ Éœš#%!& &HHc½±e êÆÕšú>s¶µŒ†0Ð:l.Ý;æ^“wNU²tÅêUo òµUÞLªƒ ·q+,OÃwå]‚'u×!ºÜÞ7,·äµÅÅ8,‡T/wµ=cwrîsÆÓ^K·n•Êˆû¤´Ül7Bˆò‚ŽŒÃs ½Ç~véûmGQ×dð “y‰d­é÷8Ø,æòÙÝ6øi].£VÓ!‰#%0á™ZÅKŽ‹ö\EfÇhM=ŒStÕÖ1˜ÓaL¡¥ŠåšÄÌ qª]:lÄç‹' Ól± Zs‚ó‘Q‡ÍôrMðЄ¸ÅVJévöÀøV¡Œ9sŒ’$ÚŒ°œ¦ËÌÐåá÷{ŽUãr¯·F+"¦ÉëòÖ`Ô¥AÊÅ„A¯¡P\‰{†œmí/晋Àsç[D(ˆŠ‡#ÄEã?NöØ÷dce¥ñ×c¥zÍã^b0â11¢ë,J–»ex¯õç§0ðLã$‘uK‘?&TÖ¹Æú«x̼[¿ñH\M^í¶ ’Ÿ™œUC¿uJ7+—êê"Öq¸*·¸ø+›y›žÅ‘ ¡ˆ‚_#%•sqy|Õ¬‰çj—ƉÅÚB•Ä#%œJ_8yÉòÚ\Ð’T+ŒÓS”VÊ’<\G“æ¨ÀTíç¦Lšéç¡ÍèÒºqÛÃ87Œ²@×¼pC’™u:ÿßgïÛw¾âÑpGJ–:·h¼'z»èoÕuœš|„'­'" Ì=ˆ›–sX¬m™ý\Ïó£[±žÓ4µ^#%q™ÑËm´ØÿI«<dÁFÏrciéß«|øÚQ×G §|í8·-Ü1.öû[¥HtÞŠeÉÐúFD˜út¯p¤“NñøÓ¾öÂ]•È×yÃéš4Éw s¯;Ì8o,³)±ïºÂŒ\25ùX·çómf½Ô#)Ï ·>LìÎdÂ1E×Á‡#%¦Žn£",U‡±íé]ÞTnöò(öóÞL‡Ãp«^ü^VŒ"Rò½¹7”$Sm­aªØ%1…#%QÜì1Gqøì:BŠE,í@ÂÆæ¡TE.¸‚^Û5k¨Üsvª{³¦²v=ã)ûŠÑ‡u¥ ÄéuØ55…,èÈÆRÀ9œ•F†–rü=‹Î;~v ŽBý×pq½grÅu¿¬¿ì/ŸñÍ c¢Ce?q©·“Éêi+°·ìJ²\<>Ó©T ,W–8¼þ(÷å§9Š(åìsµ,€âdñúaËÛç}@î{(aH¯Ô®¿ÑØZV¨ìÁÇG{æ®uů=çÉ­Û²f|¶x@Xè5èÉMUM›yp jqpdÇ] wsE"è/JœÁ<š®a„Ú2\Ìaˆ¶Ñ–|þ<;˜JG®ãÚ%u½bs  ˆQu嘅A[^I`QÙGï¶Î±›ùæÑM©eÁB…M‚vêp|A`£8#$@uæˆâº %L[šÄ|µ• Vµ Ãvø4-¹%}ƒŽÔU…=RÖ Ãy£”9y¢"0Õ€uß9m¯kxuËlÜ“Òi†NË}¤;̲Ùùéà¾8WyÅØÉŠS¤#%gY©±Q…ĽâÙ®LëÙérFtTUÎ]Uÿɨp2pñPó·EMç³u#):ð8Á2f*Ÿ)×Ï(~Ë<#%‘JJ·d²àŒ½ÈP‚¦ V¼Å6ì\&Ž3«Ñ®Ø$d[p”P{>cçãågcÉô³ë»ÃÎIˆ—îÁ ¶œ`æ(x;å?‘,V×Xñš‡v«é4„*HTÈ0,¢V#Ö.ͱ°§·Z‰îtÜ8,f½‚"ç&c‡TÚì ì òº:ìû(ÉÝRO¨´Z[wqi½?»‚£Öå»5äù(5Duo='v土3î‹$|8é:¥ÝD^¡{c(›„B$ƒœ@w½X¬ÌðÖMª<º&LF´+Ó¥qÆþAX‹‚zj½Y±G`qaÝÐî„™‚!ÆÆ ˜ïÎùÛ=tljÚFâÜKÇ™ƒ‰á‹ëòóîŽ%û‰éú"–M<¸3CŸ9ŒpÙ ^‹MdŸâž™gT>"†èîæ‚nÍ›·_j)|%ÒÇz6¸55øcOrúùt6Ï¡E'¦ÁŒC#$Œ·Q}Kj£ˆ+“Ÿb×\ÈÿË×àç›ÑCîÉ2ènCà‚Ç ü¢q´ð ü¹Ÿ|“!í¬7,š¢ËC8í‹PR”Ö†rK¿Óp2óÖùàx’Ñi‡C§ËSZÇŽ}ùovÝzhè¯B(†%·zæø5†1ÿ%Jg-†úš-¾…çˆÌµ´.Èòš¢žPä=Ž¿¿rÎÙ/ôr(óF4…º™åè~^ëoÔ&)9¥5 Áá‚x lÛŒEcÝ=\qá¹i^¼Pôh\Ý%µB[o^Ì–{-ÎõâbøDQ'êÃôc¯0[´ö]Á¾Ìž!¦7:|¨¢¾)ˆÇÀ×'Šá˜0c¢:wÞ›$ 3`˜£Å(…ºwÄW·&”ªdo¾?Ñ#éÇÕ¸»''/Ðôvðšøw.ÞaUN®—Žçä&ÏÌÔ}åÄo·¯qëª]Sñ†&^¦ýVWªQöc°0;·éqÃ}ž‘¬[šü'ZÔ#%#$½ÿסßRU¿ËÑó^T¢øô부Íi£Ù—Q4;·XrvH#´ÁˆŠÖFšýœç;ÇÌ|/<˜òë¦*„ ¶#$ø!ä|¥SŠ¦_ž¥;AÝeέ½ñuBñÑPi¸½\ª½°mEs=Æ"ë˜Ï£05ÂqŒÔ >"T³ÃÅõ´·Â×&‡Ë‡uiÚéé_œ}±ˆû(›ï:Á/¢LÖfž­.8r‰ˈÄ<2GÒ­?¿-¡7Ui«ƒhÓŒý[sú¦§w(ð¸±ç0s‡ÂÛò³×w%ÿ¡Ø¹vËê!Nle¾lÛ©j³¾;ÖO&²…R±“p)E¡ip…šdIÓXËm˜É×׳ç›—nþ$UÚED3 |;§m™yË{‘pD÷ó!«‹%xÛŸE«º5Â:oç'C†ÖÔ^!6x–ôŸr¹IöÛ¬6úC¿“¶s›ž™쥘#%.\¡"¦a¶ëÐA:M„=š=ɵ·86<kA(·77ž«©ŠîлOåÝÌk>^ÑçV¹óä¦ãWˆ7\u~¸ñeìî"Ë’ytÕŒþ…çÜ£ì˜ÎŒwÏ/W»ÌR†–Íúï²0Û>Æ™¶Ãµ®Qñv^mù‘F¬wzW¬ûXlY¦Ö’}E)} xÛ ˜læ0B;&? ËãS¦ÕI;±f¯c~—{l×c#%È?zQb…æɱ³„°¹ÁšõRLèد˜{ÆmS”+yÔ/<ÝVI×ÍÓÎÚôoäœöÛ#%°‘ãdÛ<Øk%HóÈ…pñÎó/À¼ÃöâÈx²ÄÄ.äè…·O§0ØõA†¹2a9ãjLÒ0<WíàÅ%Îþêý#%ëQ£TÙè¤sDS‰ø‡”¾-¨KìF3ld´Y¶Ù±¶õ6Þ·{HçìnÑV”ÙHN=½EÅEE4p;9Ý4øoO™$—¢?—ì;ßnâ]HëÎÓ£s¬õå-v˜}fe¢Ô" Õy,ØOãËÏR½±©ëNK'I<"ëΚC–¶OØ<UF)¾ª¨ðÛöÁI>…GM¢íÔ³# [mñ´9yb7µºþGôÙ‘]!gëvçŽÞ..élvŸ²pË”<ýÚõ\~NKa< W.FåyýUEæL׳œÙ∙łƉdî‰N‚:§'—gCEÜÎ=åóðÙã5¬è4ÏD1¡ßÓ@P`Î<ÞÄbf÷ ñà= olÙTgœéᩱ+¶Ó= $TÂø3Ñé.°›»´ý,©,jÉ®è"š{æÀºAr*„q‡Æ£»ïçs¥*”S9À ©ÏÿˆÓ{ÞBk•f«é¢¥æÇ­d(6•‹¦#( šKÍŒÝã…ó]6ÑU@Ò¤Á+\[ªq Æ;+Œ,­]š8ý³A•vÔ\¦wýÁ™Â¡ª­”#)eØ'p.z½‹,Ò¹ŽûÄ!˜¬ŒE.nMõ5j^Ö¸4K‡ì¤:œï’°¸¸KµëÑ™uP›Ù(ÿWï¥4Ó›Hv4зp~ü´‹a0†jªH¯­×©zìuòg Ê*MY¢]»,"á„h2[j `hŽ½æL,®s>:õ‰¹•[“t—<ï ‚±ÇæùëË¿»¼ž#%Ïpîù~8ú»yó¹™íã^.zaƒ‰’,s6¾Û¥|gí­ss†Z”iªºjÝk‹Åk¥ÔÒ/¹Ó–r¬/kDTsò>¹.Ðý– ¥–Ô7:Ü#X\Mt¼µ™Ê½æ4N”;U\œæ·¼õ·©&²Á˜hÜÖ;5ù†NtÖ8Á‚×[#ÜĹů.!Å)› ðûe·–øˆg¶ªäÚ¢ò"4=ß#%g¾vç>5SAöQ%#)Oƒ¹Îzù>ú½aNwÏ?âßúL þJ¨¥­”乬†úž²Ã70“êT¦sE3Ú wf¤š“%Z£¬ÔE›ø iˆ¥×Q¬I@u±|ž%hyogr¾{»uFr‹#)Á:€È W¦»DDØ,D¶åQ|[1eg#)V*º"÷ U^q˜ýÛù=<Š;Ùݵ‘öT.v|*a~96ÊT½ð­Å›¤ÁåÌsGD^ËÈM²Ön.ûî•Oº%1"]œ÷kµÓQ†O¾/ùp–0 Õ"Š¼m˜iø<£ˆyWã’µ$e¦KjuJ]^¢#3Ì›ã›6K#)¿Æ}Vgwyç—¿ofÉã½ï"Y¯š‰üª¼½üY? k‰—…à Ž Š›ÁÑv'ÓyÎÎÙüÔQÌæeÁ2D¢¡ÆœVŠ¬9­¦¸YËs€®H¥À²öëÂnÄ٠ͺJÒµ™ÇÐ'Âët74,®u/KsÑ#%½Fu'*÷hñXM„¸ÄÚ`ÿm7ç fÙÊ·ê#ma\:žÜhtÍ·|JÂDZ˲¶{„·¨`'½LŠÔ4Y5À-æS«mz¢…aiªÍ…Q`"tEþ¾°¿’ƒyÉÅQÙëãåyç5d‹K>sª÷í¬}›À$ˆ]6›Š[¶ÆnƒU[œEEîÇ93“BMiÆ>ìÔ㮸Þaq8§MN*Ç’‡&#%¼o}É(Ú<ÖÊ{ê!s«çÓW‹7Ӵ篮óÌkß$<?Mö/.v‡kgzÝq1¾¸ñ™¯dÚ-|©ÿfÇ—8®Ô:[{·¹ó®NnÇâòaæ*버Ë,ª‰P$±Ïž:oÙ”©UÙhõÛDÑȲÖL$IˆJwè¾N44†âêmðÜùmÎÐ'A½Ê¤ŠM_#)k>>=lÿ.»ãaÖ·'½dÕ•VÍðÉm‚0;#%vf|­ð©ZœûvŠ%…ÂàÂ{í|QÎ#ýö™.¯õ-§&?§Ï,›&—²>{œÕÕ;(ž‡så"Z-K“Œ-"èâÂúV“ºÅŸ0¡?zÁ¬Ý™¨êðÎZù%€ÓtÒµ|gKŒµÛ‹b2¡ËŒkÓ]9œ1ˆ}¶â‹ h$sÑCÄ#gj;i…%"ˈ,AzìÒ±,ûcÓMË„ñ•„¥$ °bn‡f{Û¬rïÔú:=6÷8áÄÐíCY˜¸ÅqÙ tɵÝ7 ¡˜y`Ϙf¢WêuuDU¶çŒó„šØN°ÊŠÚpôž%œ ÑiiÐîHµµ0Ó,ò}Y® æ›Ã†Â³u¶ó£(ç•#%3ÁÒÛm¦Ç»XÎϪõ±]Š¡J.Ì·›¡}ò}‹Î:u ‡S¼·™h#%“RÊç¹Á\ÃCÜ(·t\(…a<õg‚gì¤1šú”¨`:STÀKV!¢2Ê,²¾nhږ󪓖[ÔUøØÁ)ˆ^ŒTA<õ_×]ìçûïöè9üðµËÒûÔrö«Ùüî«Ãš––êîºÆ†ôs>3¸øÕÖ‡Ùå7¥\–ØvPòçñ½RGâ.åÛ¬ZWŒÙåN™<_ÕD 8 †zò¥î}B3é›ã®÷øÉ?%„R\}¤Ov¯z7æYÞåK»VÎÚºçT¿SÒÀ'EýptCd/ýL78—ç#%éœao¶õQtǺkút-u¯Wz°ÖÕ+­Øç:«8,j"#)mP³û"³ÝQ‰÷zÇ÷ŒÊp§j˜Ï0DÈ꺸àµw‰"±PÇé§!ŸÛß-7ìúQ§®"!0ÿm“Ãï~tÑ.¶3¾|›ki÷Î(Ͼ6àšÆ6ãâw³9N7÷00Žrœé~…W(•#$IoÀÚõ}¶™°Äå›&©J!J_OV­°½k†Ñö|Õqâ5Rý`´½DJUoë²°õ/*'¾%tóó³ÅçÌõÞE“º§ÌUG ¬»ðéòÎ<–FB­hhƒŽz½ÂØ­J™ÔEsNçrEÕã½Æ(H\I•ÎÓJŒôØ$ྑÏÁë„tQŒ¤zdóºe(/_e<ƒ7T#%úmÀí¶1‚¡/g÷›öj“;jRÔlÛ_ë¬ui¶‘i¨GB‡DS™ÚÞ¢çÂta-MMK 46ðÙN·ïŠ“)NnK µq…îÑÐÓ0Có®‘ØDD„1Ôùé~§PPËÐB¼¸#%r„oÉmiÀ+Å»EîEu˜NÊ•tGž;!|·˜¼h~W R b¢Þán‰)Š¦ëÚÄ™‡êFGÊEs\©T[…-Y!á@¤§aKNól©äuäˆRçóup|SaOdTj~Û\Ô¨˜¹›P‚ÓÊÚ.¹Ühßeu¶ÃZ.ç5]KÐÿØHv?õMùHf#$*˜IE#XÆ]qRªƒ­|)=Ej¥ÍZÛêÔsb?«Ïo%åƒ#)ÈwÊšœ#'QB4²®ù+NUB+–ü¼Æ™ .«xÍH¢›À—]s£ë£ókju5 ©›úŸ<çYç NƒF<¼¶ABñVÐ9´Ò§lÛk#$á´á%™{¥`9§Â§#)gBn¾€¬¼¥Êö÷ÈrÛ8UA”=ô D´DÀnpnQq{,0Xš£›ÂŠgYÛ3ч\ïäî{Ðë^‰Êòé=ù±@uï#%½™ß=¦÷râbhª¤¹²ÛýÇ÷óÛ8âÃêDsQSסϷ¿Ñøê%eþ åG;¯>nðþ]©ø­ÇÇytj]&uœ¥_ˆ­AÞ­jÝö_ê…~=`0: U/]’x8±Q®W­.øßµ„ºñhrÀ¶fp×z˜BÅϾ\£ß_/FµêËç²#oäqý´æ—Ã~9Ø¥øuæt%”´W±åvžê>"÷›eÒÍDTPG;‡¤öO›ÏÍ"wáG«ZüŽ'Òë}©³ø‘sï¨X…ÕR)åØ3ëüÅ:-#$œ™pW6C¿¡ÏD%#)sØÊÑÁÇw0ÈÍdT¯Æç$Sç†!’¢ óVáw:R—ËÚ 0JŠ#$ÁjÁ#%-RWH•hœ‡4Eb9³@tBžWg(é#$V¥T$¶•ã0¯ ‹heÅÖö::„UŽm#%I£T:ëj˹K»«·ˆõG!R9…*„À¡œši£“-ÊcTE!âôØ“Çݺë©NÆ!Ú¡pu¡\%¯2猶Ì|ƒ5=ø\Ô¥e¡€¤ ¬¹ÈžÞ²Œ+z-ŠQF#a%pð•M³GQW0upómV¡UhU×Ô„Œªçl}õ¤jÒ_"‹…}òñnŸå.{Á¶Ÿ×.}¾1<Y3×WÑwß*ûŸ¯<óºêløHš*{¯†rè.—±£tŠrœ˜¹¬5‘a¼Í%–'w©6SNQáÇFؽ¤m!Ñä¾’óÁ§Zn¼o×ÏS^~dc”wï4¤3"b)ÛúÑ=¨,Ãê®»j0&uû¨ºï^.ÓŽóñßPd—úmñÇ–,áøà›ßÅOŒærF^Ò&£Ås“6¦érÒ§Hp/@ia“GÆŠ¹Unh `RþÙ½‰±×«E Ô½ˆÞ´à©Ç]#% ó ”¬§ƒ¬šÒ¯Al ÂeGiÛ¸<³Õþ¸:©¦qãÖ'‡nn£vtŒâxªxÌQôõtcšÀj)¢mˆM»v U½ôš)ƒ`Öu#ŸG~û¿È’κ¹»‡€*Ç Ÿ£ ýWI¦ªKÈfQD‚T«X´wÂõöKnqBdn„¶ç²è@êÅëFOÁE”IîµgEÁÔTº4®vˆÎGÔ¼aR±Ùá¦è©5Oèů7=G¹2Ç“#)2ºVº =Ìè+”ha#)0fÏ—aD3bíVDE¡ÑkmðM~¾ŸœãW³Ž|QÄyÀm. ”‘ºQ#R¿VÕKÆBµ2*‰¡âì¶ÂÝ£Þ´] òë~ŠÏ“ÉÌøNo5))oçæwˆtàètp?Äú{ãÈÜÇ:ù[ˆï‘âÜè K—õE”Ÿ«ü,ž-zMªÙˆòò5teA#Ý@i÷?3齑+^Ý윒åTŽQb\#%`ñ€¿‘l6èfœÛ Çá„ôŽGóC¦Kâ6bÔR|˜>h‘°ÉÄÛ n–c°¶té‰~Ý&¡É^;’tÎû9¥•Œ²s];îI‰«WyžÿÑ6c”çh…ÃýŽ9èrjiÜ)á¥v¶¶#)Zd8^<ØÍW‚Ñ6%³œSø «rU»‚™“RÍ'šãÃðÞÜ@°xWºNLMü:žHíé¿nÕè€Û–9úyœ,&1¢¡@ùézötÅŒÎÞTðøWŸ#)5ÆŠYmA`#$uìLž@±†ôÖ-ÔM{ Ä£ èþýUÓ£•€P=Ùú²©j§U<¤뀩‹‹˜+fÁÕ‘›ÙÚùk^q¯§8x ™Á_MTVüô=ã#$'l)ó*Šœ±[õ=Ú|¶DgÉ=¡°t;}Ú2Ùx&ÆV´7ArøRz6{¬Aëô®Õ¥ž?µ Ö áÓLµŽ}#%€¹FËg<æRïf„îgÆ—ML:ñS:æ‚ A"¢çN¡[LJûQQÞ+J¤ðî@8Åz©ü“ö;bffüýžîX‰u42z+‚&KRF”ã?Ôæ}Ä–!\زVVQ¨{ž½£*y#$˜L!Q áceµpJpiM%Úx'¯}ßòoP§¢ƒºjO§b<cÄlÌËÄ,½>fä_({²©|ûÜä€@Ú»çóWWáÝÞžHŸ‹wMtl”¶¯Ní¥#$ #)ùHOIÁ? ~€{¹QŸG ëÞ>ìæómDû& MMËú|n¯B“{Â)Ó þnŸ§þF9–—ãl­…Ó%ýçdPûÿ¼¨qF ܶ$´ŸoZWñôTJERŠÂ,") ¡Eg¯0ñ`r1"B~œ¸^t?·äçdÂeÃ[Ò±ÞLyl'Ý|Ôøy•á!E`)$$ ¡ì!¦xXáØQÀÐ÷á™C*2¸ü|LG•;UñlõjÑúÓ͙ژlØ>Žé+ra*÷6²ˆ#8_ŽÎЬ( V>Otxadˆ—ì­³ 3n±Ç¦n¸‚B¨¡Y‚¶žÄø#$J*”C’B£IQ¹]éžßéâr=ÚJ(xþ5ãséÏ_õ2Þÿ•@/=tú×JÔÍh­(÷ ·Þ$ËCÀ¸ÀÁèO»‚¬oe.F!QáQxm\ûG#m@Ì«¢·Ò>#$ÙµëÀj?›#)þÿO¯ån—B¥<Ë´˜fÈj… ÀÒ’çåIé]Rz`àâ–*€*_‡ÉrGù©”ǼüÛ™Ás#)EVƒ¹E|vÛ€÷ã&Dý¯Ú¦‡÷TC„ºI¯W#${-þ7 ;Š?!cæsŸÙ¿Ã¡î4œß]p¬Ð!T¬Ëp˜Lû­Ùó¯Õ£âû‰W3Áq*ЃtT•¢T²;Ÿ[•#$+~áª;òÔQüa¥… nzŒÅmµ«Yn˜Š9–¢¦‹0Ïþtw:Dzl¥€F­EýñžÍ)Ѐé2P±Œ(áE5@röÓº„rˆ}³Ûì…áQ#%–÷Y#»çþ›k‚‡IDEOFÊ;çlC¬…²‹L|¢Èa_+™¨->,¤#%ÎÃ$È“ƒ’jPmHÎt‰@íа¥Ä*i¢Pz¢ìóPñD2€lý<õïÚbÛkî'ó@*‘¢úŽ ×†ÛC*¨o¹{@ð«îqÉ=¡ećÇÁíŒ#ªcxù¨_#$¯ái¦»ˆaÑ$l`R&5¹z'Ù![H‹á¶÷žžèrQš2ý8Y&ßçvˆçÄC³i˜‘ETž£¼ÈR#òdü^Ÿ#)`!±lœ÷™•˜ÒqÒÂÃìU¿,;iùUÎÿ•á›µ ’éÒYßò·ƒãv÷ÕŸµÝ‹p“B ´#$þÞ€ÿ^v†t‹å»õQÙË=æ Ÿwím¸P¯z¹ï%0F,8="å­¬Åà0B#%AÞS¸‡8éý—BÊŽÚ•›:¨žÊðwC|” P´fÄTØ¿SsWxË2Š5(ööQ—iå¢V@vB@Rª` :\vñ·E‰>0qVŽ«fN'g~>ɔŚÄ½Ãi¤+ 4AM÷ßò¹¶ *r&lÃwN´5jdHÀ™ÒâçpL¡Œdáš›È(ð…îŸ ü2u…ñμ»Žf%…ØÁ E«0ŸT#žmÕå¯Nz^¯)¤„}â#$§T+q_”À¢ÉBd5š¨#$'£ÛÌÁã|Ð%5ü®ü@˜Åå$í»yë÷»–n %µÉkÕï¿€$ØS"€ùœ?–IMȯ ã @c®™äšÄ…A†´#%ÛDhækŽàf@bT·U˜”œ•wlÒ¦E’²™F¤Þúä/k<»/ž/_ ’|P8WÈÓ&=,Ñ›»0E“QˆÈ¤ä™!C*S'2‘?0½å¾P<õU¦¾\É<¹JŠO/B9Áã©È®²›>6H;ꄲqêôåáÃ#oá¸ÚO Þ|4w~ž-ÄÆ!甩Ù8ôp/¥ÄFÜ–'|#$ÅÁÊŠdSC–˜Zöny¾À:cNEñS“—¨âŽ¾|0d1×èPÚùf>§‘;ô^;zO(0äîadc®W¸ ¢&#)ŒÈç¸#)MNøàé-r©N3pÍ!lHåbê#ü:;†W€ò3 \ä ¡­³[…¦Œã0¾‡¤ƒ±Åò&º¸Hæï¥-ñ±ö+ŒÈ®¶›û+èbq¸ÈiLµ6y–ûʬKka&I‹ü™ÆIB%¬`+ncXÃt‡ÔîáˆíÅ5·AÏyÛ,aNrœJÝØRø}Ž7NG~Ôqaq/8!Õ„åýQã¦s¿–ôÆ ¢:Ó‡Ï"æ[ÏŒã¿LŒ•ƒ²SÓ}/e{M£pß1èÝCïànPIßãËAØäŸgÛv }؇?O—I$¯/¿¤¿ºýqîë¹F«Ù‚¤îTj¾„˱–#‘6¹ƒØÌýèßc=½ñoU±¾l/ë)¦@ àPC¾G¶oÀχ:r˜Ö¾[xäü%~j2 RôÞ0ª35¬Ži¿Âሊ¹\¡–ôUºÎgP”ŸÏ".-©u¿#)l1£¸AÀIB&Ú¢Œ˜\ql\¢Ø {øÄ#%øpa²šÒµ íc-„ºƒî¨ù÷‡Œç%g¼ çugÒ¹¶ÞåaÎc)€G¢¡g&þ#)q\ƒA6*âÊì°ëü±`½ÓæâµívÜ_ ´ùh²ÄÜRSh‰i2+»–šñOÐþÊW³˜î?Hs\xâðvÑ•ËýPÄöv2&/Ýpe0T»¯Ïs#j¬)C‚[Ø $¡#)Þ:b¤\CÍidë;ÈЪa,Ú8­ïÂÅ*±¥P:¡=uÎÕò0Ó!-®øvAÍc5 Õ¾>{\Gàp­©ˆîø~äÐÁ#)8éžîo,|QÚÀèÛq"x(y§¤(´¦øËåJ®‚ôîׂ@¿È™ÛGï¾ÿkß#2CÛ§Ñ}â±Â´ß,Ç7`¶ÂU³@ÞÔÛkÆ¡Žf­W]³åEAuÖ#²ù<ó×N˜g}vØ!ÓÛÑò”ÖFÅ}Ï xL‹±2mýÙÛÒ²{‡ʤ—¤¿ÑÃÙ$‡¼à~;GnÉñÕùš6‘­§Úº)ô½âï›s9a(0.#)ªF“aó.ÐST_Ž"p±ùˆØZˆÎÞéìÉ2ΣÌƾKdJå!ØѹíT¹ø|1<=²s¦>9O{55ˆÕy¦D¦|õñ=ž ê®G¡i—3ÂfzÄÝB…JªO4bÑ®>m÷eWkZC9š÷BhBöT¹^þýùô9ù°:öãso‹A—f/-@‘‡;·‰á5tw3ËùrXn€²’„Kî×é0I².Š­–Ê,ÐZÅV‰¥áEáÅ5XìöL$·/Þû±[Ô·§íŸ6ýå‡ØûÒ&5P9êÞóŸ¶;xÅë¹P±§ËAgÙ‰äpδm¨Ò5IÃ[{ylè=ôVòcÛ¨¡¾5{#)JÅMZQi"Óc#)m9÷kòï9Êa«:ò7æq<xJÒ€Vx(p¨£ÐR  ½FÛ€H1 º0ýÛ…Y€ã¿p…Š‡›ë«´Ñæ3· [¾æêñ 0×Дþù#)âHŒêZ½ß®o^Ηíóžô„›÷‹þ¦³Ó»åì箑T ?ée¿æJa‰Ü"¥6»._¥;v¯<ò¼ŒQ¤•XÓþ_÷½ÉÞµM3Å?|·ô±™xøŸ¶Q5OÚ?›$ÈB"Wáçð«á ‡…~  ýñ} ó£Ë‰æÆpè>¯g˜0÷Â{ñõéìÈ‹V”ŸÐ‡bŸ¤€¸?ª@ÿIüŸÈù¸ÿ¸Íü¿w×ä?áÆžÉ)nx)§%@‚—À rÖ#)G˜#)|z4ï(M8Rë×–»÷Ü*;Ž%9ÆéóĤ=9Øꂹn4\I­Tœ§A nM«d³ðáérc£Ç7!ßêÖÄø× ˆœ!¯ª\¿…Pc#)MìO›æKò1¥óú(w±4°èchlH¼±Eî!Ûqm‡c~þBD†„Ë#) $8 ƒ>ƒLìÛöQ¥ú[ò?ÇÎÜa½¨Öˆ 8òŸ¤¶Fø`c9îíµ¦Á~òç­ 84—ïêYUß‚;h ˆ X#$Qø#%²;v°bú" 9¢‹ìîühÜ4ªßãR§,<ØŸOä°éÊ¡«ã¨Ý™I:nBÖ#$È¿`ìzmë$†Ç½“Ä}œù´¿¼ØH<JaE¸ÜCòÏéȬüz~øîî©D¾˜-¦Úš îÿt7‹í"xaK†Pô V%Ad f.³±@°äƒ”BÌ=lTL`‡TG¾ÆÅ9QdBÜÀ‹Jk)€_@.`˜#%[)`?lsóÊE¸dš–KREºš\¤t¹AdŒ3(hK©T&Cžë vâÐó¥6 ÂŒ¶ ´\ìdV; FÂF…L0R,\d©ˆŸ#%ÑA â¯ô¡BÛ=‡|~Ÿ?R©þ›s÷]w¡PW†æ@3A€¼°%ÌÁ€re¥Êì÷ÒøE%Ü#$`é¿`|äy¨™¯CSÊQሩkÛWe ÙâQ~äÀgÍ>Ñú/Ÿ½ýÍOs0¡=–˜È̆ËI31áãz¢tí">µòÆ#Ï1Þt®³}î©ìAâÿ|›g:ËNšàÝÃ9#$$Øn”r÷Y<tÔ'izOì2p€‰‚ö#)¢#X¥2Q*¬AbÝòmœ\"&óOe\š¤“#)'nÑQj¦' ^#˜qš²(¤© Q“çU1L-NA÷acàxÕ¦É]RÎ#)8—5j#Âq«1>Äìœ'w˜Ô djI§§íjÈŽÎOhê¨ÕeSþ7‡hø´ÅÙ’¼.ôyí6z­Ë¶Źèì,ô©»›à+#4åž¾xãã;àc¼þ™±M‘ëq¶–À5>Û94/`x›º¼Ä’Ÿž#^–x¹# žEôõ’Q¡E"°R1HŠETi4²Íi34£õ~ó߇â1ÈÑUT ,¢ªvëëÖd8̹ÌîL¦›M¶hîunã‚ç‘k+´f.è2#±¤fpĈĘ›}äÉ 8ðÂì(vG°ìÖ—3¥žß;?«Né“|[XÔ”ï Â#)B)Q6ì7Tmq2çKž€sß#)á¹Wu¨ЛÜ]Bì0†”èe†DòÚ_1r×› õ˜ ˆÍñAÂÐÃÄ>ñ„ &Ø4؛Ø`|ÓÌ}Q(ÆΆ´pØQª%¢¦Þƒ¯Ïmq²}•|jºLÕSvÉÄ7ÂKšã[ÈÓ¯„ ¸8†'‰éxÂCÉWr=‘ ¢–#%‚iXìwXýÙÁ]¼,qø)$#Èzéc# ðœS¹<Ñït²'ö(v2†I#)×Ѽmµò×5²V±­,¶’­ŠµdÖÞ*ºZ¯l:ïÈó»ßuÚ«ìûÝ6JJH´Ô…RØRŒEvÔ9 svlðƒ¨IáÛAIBØzé­ÇP3'K̂ѻɛõÄy8R)j4Š¸~f|¼>_\ži5<û“s?|y@£¥}n‡½hð¹J|&/³Æ¡ÌÓº|¼lY'4‚0U"$"€,ˆÀŠ”&Á´—ÇÀ¼ï’™«xÔÝÕˆ`ftË2/Yݘ%žÂ7¦¬ jsÆðdÈ#œ†Òð"Ú¤¥”™$‘–óÅ:aÑ y»Ššä·úVº™skï±AyDaÅrË+¡ŒI#)ÛF¤4A¼ #% *˜¥D"H„"ȵs·íC"¨‚P‚Á* J‡mîæNcôãDïÒŽ“¯@0v‚¢-!TVšôÄ"1BɵOl–0îfàʸ#fTâo¦z÷_ÂbúñF1"‡‚Œ¡6Hj‚"aN>»wC\ú6×d=}]”®z<Œ…tåÕoÅ‹1¨”Xy#%KI`ɲ‚ G›eùmã˜=‡G Ú#$›iÑßÄ’<’ø”IÛ;yHsT*u×UášÊXq¢È°ƒI#)wó£¥Y¿ÄË"ô5$@›š¢»£–w@Nɉ¿f¾˜ì¬KBC†æ#%ïÃF¤ ó=xË# å£æ¯¬{–Òú7øŽ}Ð8Z’4ªñ'¹‚ˆòB§ÐÆ,h¥!@¸O{ɹÓ5]Ý(–‚èTÅ¢…#$iß5Î@ÒŸ&ó9”b°f„‡¸JÇ%î=Á×ãæÅl~fÐÜ)¦Ä}Üjq¤¡'°d(Í´'ƒä©¢Wʤ”D@É‘0Ë,‡EÞ@¡:{|ÑÕ ÀÐæqÔª¥ÆéËc¿yæ¹’ÜÏ 5XY}V´ö˜)¹xZzwwy `6eÙ „Ù”Þ¡þ·Îl+¯eªñßñicŒ‡;%™¾// aŸIA¡½#%3Ÿ·×ÄÑèæ@0‚¬21¶3Q/uÔ‘ðï|wÔz^îÝöõ±§sá¼4=¼Bá÷D¨ûzÕÓ­Bá“…“·ÍÎó$œ>7¦üNU<¦Ô$À,&øgâ†a¼ygѾŠƒs,³#$q ‚`ÛÏC.ë•q—»K›ÓKUB¸ [£ušÅM4£Cn_‹ÎÌWœ¿Œ¦7|Xê> ‹Ä>'ÛÑß;æ×¹uH†‡[§Ï$:úÞì#)D8ëÛ݉ƉgK•\!S£›ÒÚ 7E÷vã»·4€ªKFˆB‰- ZTK=H@#)AbHéâk#$•5¢Œ¦îi9ešÑÅÌ`ˆ¨Q@œò|iè‰Ò¢¥Ój ‚°pMzvÂ4Þ§žLhÑX·J(É:C«ˆCk…YÎ_"Ë)Ȩ#)M蔨#Ý9î,ó]»íYôÖñ{±zÍÔR´9ì®ã¾o|u—ÉÔÛ²Í;§Xök)l¢Ís™ö¾‡¾ÒÆ»úàÖñÜs¾·²:£!Fê bÎPÖxÜ’âw=E†fü0Ô÷ ØTVU;@éÝ‹ìÐ_n’ˆux#͆vØ)š•PÈ·£Ñ}uò>dÞé­rœ´ªŸ™I}q^2úqä¡Kg«}'Ð% pÜ&ƒ!^#$sVE"1rœN”³q2M’»h¨œ>°ŠE=zê),.Ôœ+u˜enGpaÀÝEÉÎ ši#)öûÿŽÎ¹Ë@Ölª"r£ Q"BŒ-a3‡–}%î¸Æç’CÀv|ª•JÈM„?3'³µ{yé¹b+ê¨roå¨åZ$kìÃüãSÙ¾(l.o¡6Èîûìn<4”ç] ìS.³¶å,ÜsL“M£ä6H#%:EÓû¿LØ™çÓZ©#­i!Ñ:®Î!y¡®ÌÙׇÂò׿Y¨†ü{Ã1)1­„Vyê;KZE F¶/†o¶g#$e‰¸Çlî 6Æ;?§¨ìI•àÜFÞDx?×Âë´(ã¥@Q‚ !Œ]‘7N§’47k'•¡•k¥^÷ÖãœSaÝFq8æj\Ø@I¡TWKH"Æ&‹Ië]TšL˜™§­#%UŠC ©Á£FÛ)j…ÆMqbißSsÚs-HsÁÄ©r#)ªkD¤Sp‹'Òhû¦ŒCÕ½7n’ëóì:go8™g×.°<Qñ© Í‘`¢šŠ|*x,ƒÚ$õ%=õ_œq É»#)ñÁÕ¶@8™¤ð…w@Ýß=¾æ?Å_#$ÀïêØYÜ[^› Þ?pщ*çÁiKûŸ …®L à–?@w?gÙŸ2“B“ü¯Ãyìο,ñaèÈ¡4y#%^‰g¾¾A™¨@#)|¢€ÃáÃ+¿m#$O¥FvÁÇ"`>øV%ãóÞ‡T•¤+Bé]u{*½ä³Ãôþ9†F4O•‰ôï×í©â#$˜â#$›^»/„2Š\xÑn^ ×{ï#)qªÈšDÃï¦á.ÏÉú@qr¢z˜zñOÏIPæa‰íß¿ç˜c€o¨ÝÚ'³¨åá×xÊJ‚nÿ¥øâX¶³OÅF5ÇÔ€˜œú#-)0Ûß5òš $Á!˜}\ž¿¯÷}Жüó á0¶(¢¿¯÷ŽÎA‚fd€-RtŠù?ôÿƒÞüA9ÛAߢ=ϦÿlÔ®¯FUýGµ¯/ò¢0ìzÅŽH¼¿VaÚËt.»2‡yòˆh€€ú—¡Q[^Q»‡àß+·Ž[z¯o Ezñ®¹DE‡ôÿ“üÿwÓýæþoéüÙ?åŒ9ÿb_ýò곬ñŸe#)¹xÚ]÷ÌÕáÍõþÄÿnÚÛ#Û]ö«ÛY}ol*O_—ÅëOÞ‹Š?ó€$f'Þ{ìw¿Zœ€1SLã(}kô>c’[glæè šýÖlaÚÚôÍìç?—ßô¥¡JZ„à’{Áýÿק)ïÉÊ)ÓέoïØmÜ’d0á ›Ÿ§ÃË¿•EôGÇ^8ÓOçaÉ…5·÷Ë–K%Ræ¨ûUÊþKæO#)#%ÆFþ?Â{ŒÆ¿°¹«÷sp_¹ˆî5ÌŸÝQýžyû¿ˆëﵸo›»ev¹ç€´HŸËAÝtlÚ¬¨veclÿ!4eZêE[m¦]2‚sLž<‚ öžq¨.`@V’R‚⣎ñŸšÜ3×,œŸ_ü­Æ¯üN&³Ù°[ŒÛ‘ub¨ÕÒ:V­¢¸Â©ÙÝ;Õº)²6Qu6þ!ÂÚ^„†ç‡fbPq™Ï®ÖÍ”ÆÚD6zíuñ˜#)ºÊqÓcþ¢-2ëó=s³×eŠË<jü-v3°J#);¥_ƺ/ˆ÷ ”åE#Y}ÒϯÙóãôºž”GâU)n1ðåøIÙ÷ÔÛöÜÓ_rãce#5]uŸ‰¯è¬ ÅÛÈ­K¶ä÷<—ŒöýK™ØE]‚ #)ID~Ÿ‹ÂÃ׶ ~Ói³/¤Çý0úãáXcüPÊÔ±ˆ‰,‡ûÇ.<ýú~‹èÕWƒ|¹ÑÇ„œ6ƒ†lXS (û…p¯Ç›”Yòy6UÓú Z1Åm™`7'Έ#$ˆ(e#yÚáÃÉÜ6oÈp€#$‡“ß±ƒ£ðÞKìî¯õþ-´Î^j+a¬yαm?.¹Ÿœc§ù½+wÜ(LÌÁÓÏìZ[»žIÿ㻓ê©H"žPƒª#%£—p€N^=•È­êDB¹Ör…sZ;~¿Ø½Ù=ÿ±$…lp%þŽú=!€ý¬zV×è‹péâCsr¹Ï¤,rãäÓñáIââ{¹JkråhòÓôÓ×(Ž}`Mù†|†YûÀ#%v†ît_÷лPcñ_˜¡#”~¯ýü^Žýuˆ|Nh¯NÝÓòÓ\{Éþ2=+{âÒº°äùƒRr¸cl sNÿyùñGçÏŽWìóøOðwŒG߈ê¥|Qñ„~ÏÌn¥Ø`6ØÁm3ú­ý~m#D$•Ã›?Pïòù“B(xäñÿFã£C.„*ê¡k=wȹÕÑl´/ÙÎÇ”ë‡õé@{;ŠÔþÒñ—»ÇN¬ò±O>ÍÖlè}<Û™¨­w=7ð";Þ¾Š.Ÿ‰×i¤¤GEBø>"œåò`m‡s´éÂLÉ #$Qê´ÁÞ¿“…·¨Gvþ\¯A¥=’`–„ d¢yUzœCŠäæŠÅýDô#ÕXÚüJ†B¤'s…êfqUüœo¾Þ}“ºµd"Èr*”̹Î'.KK3‘G)žEœ&Ój#$@W•ÑȪτÈA”Ùo$\ÑrÁ34Žpê2FñŽÀÇ__‰‡Uò%Aª¤I©#)#)"u±#$¢ºKj"ÊË+:ª°z»}ˆ¸ˆ"ç‘îï¨EÄO=™?DÊÀIË#¯@#%Û…{òAÒ ”óHßC’‰&hÒ\> –ü¨ˆ€}à >¤݆ ÀwŒÜkÃw‡÷Ô@¡ùôa¾æ—°?§\ [­¹—áùV$0ü蛬ñƒØØ+÷c[t¨Ü>1½O.¦Ó "s¶^ ‡¼Ê_•°ÀO•GÇâÆ¡ƒ\jw€ÜŸîï|H Àý=|½Ž¹ü€®Gíí>|ß‘Û…i¿/òwüu+ìíëÜ¡ 'Dyy~eñB7\ýü;-åÐ9ðv—5[ujŠ Ž¼¾bJr Õ;‡'àQ6iB#¶O+¯Úì îg~ŸIHúVí­üsüA‡…L#$¨Í#)~ÇH€YÇí‚zëaÛ$P…ªæ‡¿é[è¢ÙÏb»ÍhcDr‡ ÒlŽŸ·ö+êAPZ  ˆ¨ZÅ¡Öëz৅«$uIŸ7*VÏG­ü¯ úåï.ä^’úGùk#ùs=$¡.¾ëk'¥ü ¡pNÛ̧Tï­?÷¥ùÐæ AO;‹Ûßï­BîG÷g˜Ö¿…mx[¿ÚÓÌÖ^ëÂaPáá­L*w§«ÃÅàϲ®×{x{U´As\C˜tìÌ8ëÓ°ú3W<—?Òøþi ¶æÆõ„DUÕÙüíÅËT"ðô'{·_º#)•IQ'J:¯MlsÅ^醰ù·pˆy34u½v(jPq•T_>gÉÿfˆuæîÈž1÷Dçä\ÄãÖ³.Œèê^ÔNœú£mIþ×ͱ$Ÿý™ƒç­ñ'U碡ñó‚û (¯¼ñ‹ëÍ~5U:èu劾‡€¦w¾¾Xuyyo·˜¨ÑÚ¥6ÞõŸ¾ªÇ8sï·Ø añ»³­¶‘i RW–FW´ÏÐå«Å„Z±ç–j®¢÷ct/Êá•:X‡÷©+U/¤Î-Þ¦MG¯7F‹îD#b]lì_º}qV;ÔöÆhñÎÐt·…¾Mç^{Íඌ˜bµdÛÞQþLÊ÷ë´tÛb¾/Ÿsºj0AÆ ®+M¼¸£”é´_PWMîªÝ:tçñDË?~Ä>1^ÕÖOWJª(ž›S'Þü“šÆf.VÐO‹¢vïsγÓ:ñZSAh?dfœJb.4S¸Ã†›³ùœOÚúÆúý¹bÃ(:s)0RôI¼ò¿#%yêjª«³Ñ½ùæÕ·BÌ;{ûÀyÕ{#ÍæjŒ†ä&7ô çÏ_nÅÅ¢Ñ]ErV€S’¥£I#)^ûÙrÕŸºŠëš[Kó—‚F~Kû<¦Í†É°…û5«ª¾úVžAÀlËóÖd!SLÒk=£èvÔ|Ç ‰t#R~©ÙEé÷ÃMQ˜’.á]"EûÍ'?^GpfóÛh„-ÙÌv÷'b*–Ÿ.:üñô‰ÌÄ<\K.+ŸªO²Ù×é…Š;6oÏ}Íú¹s2‡x7ÏÅÖq Aî!àÐÛÖÓN¸ßzÛ8í3‘ÆéN¹pŠáMO2Oã6]þâœá§î›œ˜¸ª•#)Mê¤ÆôlÐòQ*€ïo”üY°}Ú;´*ÐV ÕÏaãù¶½·¹8mÝúÏ«¯âÌCÎ2ñÍeµŒ+9r úÞ#)—ãþ4Ç‘A¹Š§Ì86O„o•(Y¼“Ð%Ì–ê‹@+ïÉûÌÉÒ°n7Vª.Ï^öIßSG…Ì$‰A°Bn~m¡ZØRC3)GñÆYÂ˺{曡¡–»óMKýÜX°æÈ8ãéé#%¶ÎËHe[¼‰SËýH…%"슕ô¨G λìUµü£Øf¹ëõÙp²{}³1µä¼ÎƒKZy3^åÎêÖ"âXò2ü¾Gôűôó[x'ýq#)Gër>6®q†õŒEfXÄJÚ=;³ŸàÚ‚ûká-¿Òü£aÖ%϶_Ö߶+¼äâ«ÆB¯„rMù[]¹4íÔû\uëão»½ÐoÚEþ½Á¯Î×%TdZ@Vàõi¦LŒ˜eƒËõ Ü ø½`€8>¼Xï`·b ÑÕ61›YŸÐ÷H ºššÓÞ7õz°Ç»“òtmÓNubÌ6þ="ê3'•’–¶üJ¿?qü'ÛŽ¦S¡Z–0«+îùÔògëu}âe6R_™â`ó6Å™¢àˆïÀ„Ð{Ã/’cõÑà@–L}=6ùã #)H6œÔŽ8{ù·„Žµõ{~pÁ}¹ü.“› ö²¨“˵ì£AÙ„¤¤Ñ÷¡gÒ¸3ša¤ÙÁ0WÁ¥åO£ãŽ%×ÐL´óòüŠéÕÛNÎ?Re(éò‚tâaâ#%·Zô˜åî<;#$«H=É ü·«M´ Rƒw+ ÑaJö0£ˆPڸ朒zƒÊÀïí†#%xybXÌ|~ožá¶ŒÉ÷U„éå¿åPÔ˜ø è6Ogyþ/œ×J‰ÑOñ ÷Axr[ùg5¨ù(龺'C~·˜é øLŸÆ¬ó_^#)óÖÓ²#%©ÙŠÆŸQ,iLКvq×8™gçBzÓËJ†Œ^ç‡ÂŠß‡×&SÖÃGÍ«ÆB6ˆWÓˆ½É÷ØÈl°n~·õ )ÜÉ.òSÁü›;÷JmuÜ©VÅ@ÎF*¨› ™ÕUþ`Î×âIŒ´ Ÿ#)ãaÝJN îaÕ—£‹9£Ê¡È¡NŒ)(}Òv8a4º'¢‚ƒ#$ÔVÒ¤Ýg—i[ü7ôcæ <¤}»-ˆáæEºÆ­ß–!©ö{eÕ´zã´± fA (ÒŸt’Iz”«$¬T#$)‡Š¼aŒZ¢Â—€ãr?t*(¹Õa0ú¥'I#$ 2 @‹e5r’È3ùÜÒª!Pòy%7Ó³¥ô:‘­YÑö®E‡½ÝðV+Ó›{Mó¥ø—Lw³-‹™†qÑvyD©†rõ­ë#%;‡z?4צ˜ÅC­Í3µC逎9c›÷}cÏßu:Ãú’ízÐör/º„!vr¾éˆ»¶`è!†wcõ6}Þ¯ÞBLM|¸ 'ϹÜhAûôŒd;cù.ÐXËüÜS‡†E‘aÀ–âK§î ºX‚Î>4<~Fê”W_È§æ ‡²šÂö´#)ˆ~â'ΪÇqOèÂè8g  ò[˜w zõmý¡Ñp% p!£~‹; *ŠÖ@ßE“Ô3ás©.’#$Æ¥gþí_þ¡9€ÂšÈj„>U÷g}T°¿­ï}ŸçÕ m¬è`ÇÉùÝ1P<+Ž(o¡!ˆýFtNb)Gúhp²ßà>ú#$¾ÿPdÞó[9@jÕÌ Rˆþ fWAâ¾?ëDÏÇøÉ£øEº#$OG •©§¼_}•|nؽ#%ÇSÒפHýîòǘjîW>¿ÙöscÉè)$¿”äå`·æÌ<‘FûNwŠ]Cá<eŸ¦©P®æ#)‘ RQ-ðAÑîBÇì-ˆDßAKQázÓn©cÛ¶žŠã÷GÏ‘$ò*Á‘ƒõüGíCéçõË~žÞ.A˜÷ö¿âX¦ÿ™|q€™ŸŒßòéIaÔö?ÂN˳àüÅÄ° ’s l·Øõg}KÒ§`ìûój³Hòb |„HcÄól@À|eF$[EÅÞ÷=¦|¸VÉÃ̸“ðr"H‚#%ŸE²óSžòáxäƒÛf‹—•FŽ+mu¬>`´&ú—"ƒ˜~ÌGÄõýÚ®ªêwfÄ8§ÁÜÁ5(jðTÛÔ3”×,þNN!J`BUF#Ö,¯ L’œÄHR¨)¦¼0¢ªCI»¾¿·ñÝõpÃdÓ»aí9úÌa0BD $O4lÚ)PPíîúÞ)¨E0ð¿,èý©ìåÔšÃÒ?f`¿„Á<ø8鎃»ˆÜÇÓ\óžx_Ë «áó;y]6DÝHvSdH;åî’H‘ EÆ^ƒ¶/2K #ŒDNZ²/X¼®tæQ& ýòä#$=0œMïžòkðÑ@¥6ÔÇ¥ã¥û­ÍÁ#%¿¨kK^ýÞ“V_ó`wÊ~ج»…h#$³u):ˆÜ3o¼M„–`Mtv=ÑÚ”ËLæÕ½#%g”©ØõdäÖÜy¯ž“VÄØRŒmʪç17F~ŸPibÎo{T;ðÜ7LÛ,®&»©1Øfe7ÖqÕb·Þ`wbç×äÐS:+ǵ8í\RÇ`i>Û¤94©¨ûA=}ø0­½üo;u˜èà ƒ¸§Aùm”P•'@î}\ò¢¦Qˆ€^-ñƒB@Lî½PfĤÀ³e!#%œ† ˜¦ ¹‘)øbñISBò¥YøÒúG¥›…{G>±¬.´ Áè4aëã£~í¥aµï†¿)Q@Ñ(P:©”©mA ešWå‰åmc5 Kù—Ψ¡#¾#)½½_‡›ý,ôe3Z ×Dæ‡WÅ ³Ìs½réµ…„=o?Ù¯hmWÓ÷ã{¤üt|Wƒàøf!Á¼Xp=\k©Â‚š¹–³õ}«Yˆí5ŽK_þnÿXóhÐSÚµÿ(xúà§þoñËÅN„’WŽµWídJl/^•ÀòŒ 9ðÞ~%V¹ÔýØÉô¶,¿ÍÕg€AeLD®RDd{$!c“•SÌ\8t¤#%¯žàø”#).$¯­Ã½ÒÆ8@Va[…©E¬û1KšnùŽyôÛ§îG9ÈoÉrŽªÍÝ$W®ªU'I2gt©/ݶ‘³kâœâ Ùz6,•4éœ!Ufœ¡Š$ë{žû<_ã•)gtoâ´C-žnÁç¿)d¸`(ä.¢V¸ñ(#‹¦1Õ³ÛD0+^7½'k…lò`ŒdÀðV¨jÇŠ&døý—Ëe’ÜñÚŽón¸\#)¯Þ|ï¼hàn#%+yÓR³:ÄÄ÷N'zŠP £sU‰–=QglZEfØ pýê/©þ¡È0 ø#)â‚Ìÿ0æ¢z"Ôæ2Áe¥™ìêVº" dÐhŒ‰Q’i½vhg*ëa®‘±²¶89¨¥¤óˆ„¯O¯4fÓ•†2茿clòÓßÙ‚2¸Úáû;'¯"ÿ‘+¼“Ô.’mëXÎïaÜÓÍiè¥iÙ²¹¿/t¡ïßÕIÒó„«ãïíÒô»z§öâ±Hf`ÌÂN¶»±sJ®I¦Ê¥6 u,§4)°©¶ÀHf¦ØX^+¸Å–Y-Š¨àd!#)…w&ó0M)dhÀ Ê÷» Ch7#)•Hj8£PCÊ^,°kÅHŽ2ºÎC%Ï7èÝatNr™oë0þJ"RåìNÓˆq†™H)`%\‡ðküù8<½<Ë{Ç#òÑnÓâ|Àò0VÆ5‚“‰ Åiéè{ p Yûý#%*G²¿ˆÚ8T°MˆÀbq¥¹›ÖCúw¥‹öÙgâ³#À}™õ«ôX~]W§Ë-åöü~xËŠEÚ¥ š>]µ1Á¥'f´z¾u£º6´í„ÚH…?/¾wƒ¤}Úœ¤…‡aq´Áäãdí*$öÎ;|QÜõ‚îœv?¼„ÅX{§§nª!ù/ÙÚ~]ÙÊÖ’HçÅ0J×ÇDi—.pϼ «gLlCgø+¡¶KÐòÅÕ²gµR·¢¥kYKÑ‚¸!(M…i1 züÓ¶¯Þ#Ànª»Ð0¹P#Ë`³šAdºåXóꑱä\²EŽ™‹ =¿'›KìœýAÊhµ¥ŽlG¨‡æxX(8zœÒPâ†#~.½3iÒ<#/EŠÁ)U‚SmÂ8 Íåµ(ôÓÔžO.‹tI)au0*Û¡º!4ºOÓ ¤)€á‡g§aóV¦ƒ¥‚“ëž__ DÓ'8z:õ1=Ôr§ % $ž×jD¶›U äÏ£Ê-2§"lU ´_ÞbLê¸óûYOÁê=¬@…·^·ùìü4-<£‚ŒU¦”&;Ïo…^âëBP&œÈl°¡DMú"vŠ¯aTçì±j@Ö94E/}0·]«D8çnÈÝ 5²¸ U«—µÝ§á©€}]@W:ÐBœý,áçÁé §D;aA¤ˆù®­õé»f~¶¡ÔŽç[ûÜô‚ ™èëXP§x÷º|æÐtî#)Š@zJž$ü.{%™ï¨3¹+n^M­jP/ËKY…¡­Y ´sÎœ‰—]U`r€Vë"˜ðh¶œ‰pøQÙ—pœ4+¨M¹¤åÇNFó $)Œ_Âêl#NQÓ<õÅ% h_™úŒ1øÛ5ã›ZÂìouX×Ò}¨«ˆ =Îéƒ#%T®Fæ‹Iâ)|Ùò½q_–Cfƒ³DÖ9>Uoº![úÖ’4¼bBâP Ò¡´ÌGi¢%00G¨B°À8Š§Ž€v .¢MkäÞÛ£B<ãá|óâø °m<Ïh¤]¥æ7rÙtST"ÛP(aZxdîÍ‹L|µi±î#%spW|S:nˆ=£ˆ.¯:ZúÑ´ólZì&Û‚ t„‡-O‚ ¥qß0<â#$‡è9#)*ï‹@h<b”º¦ïîˆkô ^D±x!Œªìtæé~%óX¬ íçÒciNN^dÑ<1š/iÜÒŠŠpTP¸À3ßšcä}º1GÖI»¹ê‰ní ˆRC™m¾'‘±¹Ÿ hïîTyôæ—2«ÕÃ8³ 0ÂÜÀ¶=NËšô(춶Á£Ï µ\²:ä±Û]LéÖÈo©uŠázãÐÎW4ˆu¨†…ºÎQU÷#)ú¨˜£g@îª#)S‹ÐRÐÆðƒ6\×¼S{éJª\u#)LSÇ›ªrJh¯ŠÛ':ˆH«FÔ|­QXCJÔ¤#$ö½@Ë5CdÄîÕCòucë-7‰=+1¬üέ¹£6zòÐѶ*œÍÜ$ƒ—_^FGJ1œN³Oø’¸¨a6àDº¥Ìî>\Cμ³e«ÎCÔ‹fÈŽŽ‡Ô©iëªQbŠy0ó½ªQ¹ Gć«%ÓL¤°°ñ, ÊÚŒ(œ'8Éúö4q[Û5ÊE—!ASÍ«)Þˆ¼4'Îw‰QðfÊ9~¼x:Ø`ÐQ“³·ˆðÞ-¨‚@®4á#$¶B«n®<åàäpPœ…X˜á¦›¹1Ù}Õ`+CzÔ@¤€RÅt 5:}tÆuQ(zÜ÷Ø·¹ ³ßë8ÙÞ1¶é#%Ï?™‹Ê^PÖâi`Ùd]ÇÛ¤Â×êœuéƒ æŒÇ#$,ÃOq¥Ô-ÂËn7uNŽ¨fàƒÖ#)#%V(ª™1šy5¬žuæb†´AØQ/ãuI:ÉÕMwÓZn@lêx‘ʤí¥ÀR馵Eº´AÛm„ôg†I˜ "Ë8ÅÙÊ#) 8Xí#%OJÃtíïEçÝêó–rÄ÷'…(ZÖñÖ/õìƒÚ© òé–í@„Q»w nyéT[@i|¬ P#%  7yí…ðº«Eq  Ð„y,éžÙÛª¡U£Žxm†RtÞÃ(eŸ7ò¹ U¾¥ÐtÔC~žŽf\õ-u“êtCf&àÌ)«ù}ùôýÿÆ=~Qëú2‘ʃ‹|Î_œ:”j>Œ•Dÿ’Ë'ÛÓÈ|M)©TîÈ"#)V±îpúcé%•¥P*17B£\xÉÄÔ÷è:Y©Æ½0GëɾuŠé¾£°n#$“ø{©­;œç§0Øx0ð‡ÂŽ«Ç@Z#$ÉÙñ¬f¼ù¥E7«ñüד²¾Žl6þÛGáÜ<š„¯¿õ½bÒ•˜^µ›³_c=Ü)DüF(~&Ûm9®Ð4†G'ä/wCŸ~^ÿ˜U`ßCÿ!a@›Õ¶èŒï :Ê¥@"qÃv?ñ3×_wùÀ÷áýÜw}áû}ߪl$þ”?œ(2–#$°@ Ï®pj‹+ëÒ—ü’Š¤§ûgø‡, !Û¢¾hó0‘Ô]áA±(²· À©`ñ¿Ïh1¸ìÐ[?ÔÏå-㮯oeŽÍ ·;s,6ÿTßdÑlí¨~!‹ˆ ä8<º‘…öóí7#)š(&Hl:Û8œ¹‰^®fÔúüo6ñ`§†%#)¦½þä,ΉCébý Ô}ÎðB©Žà#$¼Ÿöò[§Xx5ÌG¬{AØ`ÀÞô{¨9žïoû_C“ó/Öæ€9šCÔ.È®Q_{ƒ¾Üu šYiØ^åÏßwûBù&¦@6ε ±ô` >£RÖBt›c’`dže=kÐ8 þìTûýK¡ÝþnÞìÞœò~„ÂÞ êŸñÕ¢U­úeöãÛtŒŽPKM=¡Î¯@2q#$˜H¯)å86ú ’HFÓ<•^¯d­ÒUÂ#zX@ª#$ ÓÎ(•¤skíþøý~~Êì#?m‹nÝd´ Õ$o—OŠçûï~¥nÃÚÅÌ ü$IŠUW\#)–.û–\>h“Sé4n)jJað+|Á·€¾Ä2†ú°9ÿ¤–­†ž!à†êü¡Ã«÷w=“†Æª-¥ˆ`4¸ÎŒ7qÁ&ÊFü@"äl57+#%aSíE8‰ãá±4æ£]ˆžØ €DþG¨¤4§ÏÊJ;HÉøÄ$/ÓåMW+6ç.I¿«ã¨€zƒ›ÔG@qÑÖÛ°bÐ}¤™ìMv#jüèn;îäk{J‘’ˆøÔ°—Œ PP‚‘.˘#%ïap]¥(ÀBÉÓé-ÀŠsw1¡z#%r$s©V6Gal‰‹9'C :ÊÓ#)àóÖÅü¨h4óŸUx^®RO¯é ÜŸãòðþï/Ê>‚¡,ÿ9PÀ<fÄá—º…D¢†…ŒÂ1¡ÿÈF¯þæóþÀ:觘ÊKïã–™ý_æãùSþÆÕ´‘û„³ÚžÌ%'èÁヰ;ÁcÿRã]÷«Y×#abå{ü·¥˜ö”dRHõpi2¦Žtµ¯ùëËË\u[ï»nѹ·ó£Œ†‹癲ÀÙá0"²HÛ>?rs<i)X<tõò¨¤ªõ·a­êŠQ´$ÓéZ©QvcÝÖ=à —ëÁÊm28{‹wínÃa\æ–'ä1Ô‚(5„N‹_Ÿ· ºÍÐ~©œØïü3>ÅýÝ̳îÔ5½÷òý‡Aˆë=eL¤£>Ü–Ô#)Xš·’Ò “à4žß'C’?ÃøT‡#$ü(R˜þ#ÆåA%S•.¼®zÍ­îu„:”Î&`t”àÿ¢g€hç‘fÌþw‘ÌXôƬ—kçÔÀeî#%J4k«Ïøu¢#%dt’ FpgŒbÝ’K!V.íÝ$’H™^«¼ùû6®(óÙ†8°àصß`ÿ)ë0Þôo5í#%^aÚàêFA„#%Ù=8üLa´è6™.pw;²Í—uK6ÁÄ¢Ì#‡š/à{öú½à]ê4 WûGö"øœ OÝýÛNÙò:žtÉ[L_Á¨4\¿q€cjÃð]þ ñ¶ƒhÉg©Y¿‰÷½^“çºãkÆv²#)„Î Ÿ›TÛ|ƒÞ\IctÒé‰bŧý^ÝûÃ=<g°ò°u:—¤2„´DÕ’¡ásëŒφ¼/º:iŸK´ˆb©?¢Qô-¾Œ¡ÂØKH1±¡ŒM™$Î.r¾x–°RÁ¾(4#$D"ð#%i`øðÛ¸?z$ òï¤Ár"æ\taðVƒ/§éÿgB>pú:{ˆ„J¤„¦Ez:”ÚnO¯CÔD4;#$ñô9·'zÝã#$¬”@D|.aŠ"©£Ç{X¢º4ϵ,ø#%ŒSŒé}öTZR¦·Å‡Ó8«È½²d¶K#%¹²¦šMaQI–$Ž:Ñ’˜1èddh Mž1§'·¸¯´54Œx°>â{f ³É¨…#5ÖçôðY;ƒ'CXpëâ, {^2nfóf!Ž¦OHZéN%ÔÁˆCS9"8P\r!õ¿áéüçÓÔ?ºå»oG×Ú…ÃùþþYÉAkZS?Fl¹®î?oÎ5àÛú~[ßz¯^™)"1‘E@zo‹ª«¬·ý,UÃŽCfù¯ß»Jm”ßb•µš5­oC"z Cˆ*ëœDKº;6ñ™2¸Zi#%8pvd’BI›«mb“3ZÍX\ɨV]YkÞõ­ÊÌ ŒÆMÃN°£#)=<[y¬J2þÜ3a7Ã{Þ:²Æ´ŒÚÛm¯òE Â9L²ãìøƒPYPI$9Àª!¢ ƒÊ€#$ÿ™ïêIƒû¶\9;ø7 çœGär¿âþŸezº-­ÿ3‘žZ"‡Øí#)¶©\ËT’}5{5tˆÄó˼ÉI~Šý붼Bó»ØŸR>ß­4ðàΔ h}„:;í lF,]„$$·|ÔºåvÕõU¼>ó\È?Í”Ät"š†ÒJwÔ["t*»ÕÙ¬$ˆ €r¶QI´|àÉ’tÿ»n5Œäй5ßwu¶ÛmtT¡Û1À‘·¤¦³·DÊùÌËÄ¢}dñrTÒ±ÏJMsá/MÌ¿Ÿº19l<ñIýWêÑ{LjçÓ.O–"lCÈ2PZÅ›Ž—ÚP¸vr 0yò‡8È*Á@A Åî_³]:µ)¹£Sšå¦ÈF©ß?V±Ãá#$ø×ÉöŸ`|ìu7}ÿ»+hôvùzúŽÀ0¿GéTžä~–1‰yrŒlV$õ–»Š÷¸? ïÚ·¸Së"_Úv|3çgŸÈª*T”¥"ˆ§ ›ðÏpSF[¨5º¶È7ÁVÍÝ›è@1ÆRðK:P"b~‡ÈߟK]Á¡wh?Ì‚ßÄ}Q8›þsˆñœ¢½ÄÄ´ f"y–ô–‡òÅ-î†ô.¤$ #p“iáàÉ``È3q°šnãåuoˆèCO €¸YùlႧ¢“+@#$„ ñ·ÝôÏuçò_î+¢[ç³nÑüGÑ¾Û !)†rŠJ =+ÞÊO|\B,À6ô½`)gÏB£Dûv‚y:«Æ/Ú‡žŸëÅ°ùŒ¾R{ÑÜs£§Ô4!‡G¿0ÙrEŒX¬Àæú\#%ú½ÆÓëƒáº‹Óˆ½_¥ùΣäÙTåcÕ?=NÍÉ÷›Ä8@w+ÕNÂ*Q8#À#%Ïb‚L&KFÁûCÂÁ€ ypéê®>óÄ8»š0Fǘó™òÚ´ £ÀŽùÝÄ©kOõöj{{{!` ¤"‘HÈCÚuƒ—ÞYéSúŽkuaܪÃMž:\èC¹9>v†÷‡ 2 RÇ̽<éI2yCoOqUôPú:¹—ûqõ–¾$ÿ-´1Å„ýçé+ô“å>K·­éÑÑDÑ”=áù1»ÝŽ‡_ÓùüO/WÒÜ@Ä.¦¨zTÉ@³òÂ2I$õ€\U<‡Ð·÷fV+›)@H‰çñüâþ “è:‘N¨àl hiÙÐ{(¿ºÇ/¦QÕçh€œÚ¿nfµkr­¬D÷b07½ƒ2eÎ2é/†F>#$0…xý>èì10GúUIX t|C<wÏW~a‘gv}J¯äcì<+¿„¢]âæèà¼àS›€±8†¥ƒ™jºoÀíûŽí‚Ç<Äã!’ÎÄ`"O[tž‡³V¢"R>RQÀɹ ³öÁ.&Õ#ƒ:ë“„K‰825–6ÆwåòqÂ÷rý£ø;_W»ò„™´¶ R–6e8nÝ3Í0”`Ì-u‹r6ÍèE±½„îzLy†çКtpp"˜UÜ?¤ïè™aƒ"H,ŠÈaBH„–½<øs;#)¢JD¢‡éätaéa*´ µ‰bªH’´ÕÛÉ_(>î?YèDð < ÃæÉïµp;7ðœÃR#)ø ö›NÍŒ;ÿ¶o É^jªQ{žÏ™âþÇ×_ŸgKS ·…àÕÜotcz’zä·­?– ñÂÔªhrOòÊÿf«(Wÿ=Å(ÃkyiLÕF9(©t½àÁ ™¦.š‹kEª¥(šG#$×ÃæêëîžsºNÙæµ°>óßGb˜)ɧç¤ÒØ=§ ¹Y*Ö÷vÕU³Xã#)¹Gö&^·rï4|†Ïd¡SØ#ZBu—ßøŽ2p)æ+T(¢ Pˆµ>"w§é«\‚(m¸gZ¢ »ÁŠ#%Å|‰À.ªÏTì€Ðä¸K‚ýO51ô/™Þ+à6R4xènã7ôò€Xö—,,X!ê(->Â/ò1Ÿ\­Z-¡@ò3”{¼yòþ"¡ü_Í7îŸ:=Œñ³ ,Ϲ‘¿ªoBaMK•àfšÞa—Š1+&¡"$®‡j p@CPî“öÄ„Ilƒ#$Ê«s2”¥õfÞèbǵîJë=ëhµ~”® )G”Õ(DrT#̇㊢9¦ CF¥¯ÐvàyìËÚ‡™þQüþ9dRdƒÀ‘’H¬6ªÒÆÓ}¨lÕ<áøD§nóE25#)>TjY((9‘'X´ƒ!ÏûŸÑÀ:<û¤RÝ/µyXµê:!…¡çöÒ%ò´éª«) à}w‰Tj–EdEE‘dJfQ¦úû;¹­zÞ:­ÈÍ*Ce²·uç™u©åÕKD´‚ÈÈÄoNAåôz>œ¯ð‚oQ%лëòž`õ¾Þµ#)#)#%ëãpÏÚÌ EùÉ#$–½'¸€X2W“ÀjlâÚaÖm~Í«!ú¡CSj"˜B Š#$PL,àÜáRŽ#)CžÓ4,D çG,-d'ƒx‡š¨ºîäìÞp“ÍŽþx×^UÐ/Á~ Z[!Ú4“fäêÜŸž}TRN5 7vI‡:ŽšUÚpÊ:ˆóûwnìˆVñèh9óGc–sÇÓ;mÓoªÛ †Ü‘.0#%¦!Ài» g1‡šÚ{.#Ò'—š\%tÀ•Lú#)à–€DøþW•\.[ºjŠEQE¿…`©þ0?‡Ö|Ò¸ª©¾ÎX÷q}šì!ZÍ|Á½ów6±ç©PÿÌôclþ´MŸ`´*ÒÓ0ÃL˜a© ‘Ð9†õ(õ,’¬’HúÜûX?N#%áëè¶ð4 4µ€jbA Ì©hxÆÙf"ŸI¿‰ðãE!ò`Ciš:1HèQ±ÅÅ åÓüœdDèÙ±H*àš`0Š|ÀPø;=>ÏÄ~þGQy0œ³~Ôy‡z¡ Ã¯§oLJýÔQÜBØ0CóÌN¾_.å}|ÿ®eÆ”ñl+¤~îVÈæñŠÈÆ()(îOS `Á•Àd‘a¯®èÙ¡¸ÃgsÈBø—Â}gÝÿ]} Ú±þD³Ñá•#)ÞÌêû~¼vsýâTþ¯©òÔΰôÊk‘#%üêi’„e ù”ª´U£²¶ˆ‰¨@ÒBæÆgù5=#>úÐìPOg–K³V0\###T}Œ(æ[îP´Æ¸“‡¨H“‰.b©B €¸K‡LQÕò#);»_Ìíȉ@çЫH¥,A ‹€Áƒò¸.ØïÌÝ.<Îjäž cù~°±ÙpÖ}0¸rÝÚlÞò#$¦õ;ípp"#%€:ýU’èÁÁA2ÜŽ—QÇ9†/§nƒÒ§0æ!½íÆN;¤²‹pýAÀSP jèð+½77XvÜ1S§#$0óÄ:JåEXw™Œ`ÀQ΂½}h£W6xtú¸B“ß±T(¡œOEØ Ã#$#)ÔXÈOçÚâï×üþÜjd­èÞöÄú—m™ífÚÊ韵òÿ¥šâ“GU/±ˆü˜Iâ:J@€Ø¯Çéµé$U$M()‡jC&Ó×JÀ£‰Î{Q#)Ø›—óÎÇõ³œN«0_žÚ¨Î`U‚*dýi(®Sòª--lm,L»XWW#%òû;„sª*ã°wö§ÀËS7Þ3JxqÑÿ³°ÚbõûE]§*æ¿&ιB§ W‹Ts<Àû{Df“Z]OBd+î(; ½xóâìDé‚È° ²8%vÛx„ôÓý>¬jI&v!Þ¥jpî#%æåÏók{p´É6^@ä¨gCóSQ3;W‘½¸Ò0!2#%^6‚)a€ÂJG‘¾ôWß èCÁä¡×ÙĜгÐüBæLŠB«Bª†ÖKš9éãÔëÔ{§ŸÈÝEÚS(G™ùBlÀ&¸ƒÁQõº!FéÔž9Û»‚¦PÞvŒÈ|h¹Bù‚ÿa*2<~Äkd‹¹{`€¾¼LMÎZ„›b€~Dû~‰Ùçówö–œñ?¹¡nš¾Ó£o¯ûÄ‚"ʪ#%•BÐR8¼‹ª <{#h`´OËísã؇_,øúÙîWà×é#)Ÿ…J‡¿é­˜ÓFÖ Îh؆ûóe}ߢÀû©9F#)fƒ™ÌîðfÏ?ó~›4ìë fñ`9Ènúªgêí¶ÿmYkíÙ·ivýĈð§>⦨¤ˆ€MÐ!ivB ¨€€H7@h#%‡3#%z$GäÊœ}ˆà…˜oZ ð$i–££ ˜ÂÀXôPŠR|ñ÷w{ƒ·›0ÃëCh£®##—‚,`²#ûúDàêê‹ÚG’óyv¦=Å@Yz„ÄKAP ÓÌtœ½äÐT6üß“cqôæ6O@èSÈq¯,pž2‰Æ-Žƒ \«8 E1X?éÌÄŸx=FÂÞVb‚ȶ.{D‰¹¢v‘§°9.ÚHk) Ø0–š…~>ÿŒú#­ÿ=çÛÙ€V˜@b·Î]×o:⇊ñwkhߟ#)i™õÝè1’d%ˆ>§ÄØCxÞÛçK4ñn0×#ý¦te8îg#†µ‹Ár”Cű¡xÉèñ»èÜcë`•íÍ<£oW×VHŒØI,×Vΰ `«Œ#)5ó~-sñõË–wiz§ž^DX’Ê sl«ÙãèD<¥uÚÃ齧?oê´"{Ã5ô ÅÈ ª¿:X"˜{“ºzϡωÄ@ÃãÍWß×í¸¼ˆbŽßŠSpý¾z;½|JE”B ¥wý˜êN#$y3ƒòðI‰"—·ò߉ý§]βs*íñDG­[j"‘‰cÇk®3~ݼÝÞZ¿ßVòšÜ_êOì౎@á‰r`*³¼ŽÕǶ¦ç & Ö#%?~³óœD÷+mQ±Ezõy„ëºWÇv™K}o`"‡âI¹˜|‹óÿˆŸÛŽr¼ùøT%4¢ÔóþŒ(ù–ãó/Ý7OtöÑ*£åG<š¶×i&ç)È&䳑ô9{žÃ§ÏÆò~ÉBy XK}2<<?œ10÷cþˆÔn†˜öö@ž™Fú¹ðj`sûz5\Ÿm4b~ˆHÃì{Anw˜ôo/v½ó¢¯ûu:=E5€Hg‘PÊ:qü<mìfÏåÛàn„š*›oaØúáì#%:è/“NûŒÄ=°,ifüËáHSA@ÒThž…•'/aP±@U„XAØYÚ°ë>aAÙü-³ú¡Ôll#$Ò%ˆ»½ý\KäHâG¯ð? }Ùv}æ—k„3®Ìí`ÓšÖF]€”‹h„ !7=g¬w=Èn B XQá¶û¢»’²v?}îK¯^”Æñåò€ä9æþ}*?¯O9rõæ®[âÿ+•©ãørCNÀÿyCÆ3ú¡#)àÑÜÐALDz™¼ç]3-“ÊgyånØFÞVX2h¬'à2dA`¤À!Q2¶Âjjú‰˜æ‡q]*j…‚t ðÔÂÒTÌRȲ§šØ&ÔþóäÍ Ô„(ÿçŸÐ ’¦¶c¯aj1ÿH~Í¡f(-4ùê"bâMr“ANìW{¢Úi«Vn#%èÙŠ¾á‡hF {j#%÷D™q+Ô´çhUˆàÞ Ä€ße(eÇÌĺ^TÀ¦~®“Oñ½ßù|µ®lY( {2¾"£Rñ¯àŸ† )þ“¤ðU+%E÷¸EüòñPÄÈæI:ÿ´i3 ÈýŸ£•tWþ|ÃÍë¡ù};3˳ž¹Ó§^X0¸þEA¡Ih²ZKH³N-N½l‡ûû”ušùßïþ¥ŽpX>ïOÿ™¿ØyömD~ÛÌâ"¾TY.îËšï1òÇ{ë×Ú:yT{,eÃMÙ"bŠˆD!N_ÍÈ"oñó!N¹ùsÚü„y£Q‘AX¤,Ć9ý.)B‘Ôè삾×%£¥“Å@¥Rð#$ œ0¼*5#$@åì'ŸfÁH“5BmA$´‚Ÿ¯ƒœ`‘UãÖðž$Zä=Î#)$»ù@‚ÃÌ¡”-Õ»R¨$#)hî‚&ý¬ºsR?³œÁàg#$Jª¤6 @ÊÄxèF#)GŽú¡W&IçýŸÈìÁñëäw$!¡Í·þc¨¤\²L¹pzï´´Ÿ$àlCÀ¾ñÁÃG³ïƒ©·´êâ=ÆÑÃSQ—ä_™1›¬äohü,£ öÏñã<5:ý°Jüµ&/£’ÂKDÖÖ?bì&Æ8g:µ‚¼<(NrÃ{)t¾Á5úÉï..S­b<‡Q´¼>\‚€áwAÈ¥r õ²`vN¶?ÝÍþ”ÿ5ñ5Üç·l<ç’[Ídm¢†k$éÔþ{ ¢á•bC¬ ê…­nÕ-eòt×½àŠ(›Þˆ‚p ÐFÙ¯Çúõú1_lþåéz…è/ø^§ý?ïO`«ËBòþ7f d#$àç²'Ä+T@~_Ë¿20A/ÓZ¨PínÀ‚ª9Cm?}ñó¾o·ÁåÀ9Ñáøªô>ëµQØH>ÀÀ"Ü]4¸Î¯H±µm䜅ð{³´ QShr¨Ueæ#%v1m£Q£ g à>»„:F+¨ÌÖ'ÉÆöÆÛ'Ðïaņ~Vtš.(Fý±#%÷\£ü±Æïcî3¨j|jÏ%Ï£å#$pÜ((¦„A¥äøÆãÚ\îtùϧ£ÚêÔz›¸éx‰àŽ1¨¾j—ÁâZî£Þßß!7Fí¶° NÕ;öuŸ‚ø{7‹ —CT.­”9XðÐu¾úèÐ-Wûä‡9@’ßIÌ®¨Ê/„t_ÙÿµÚ(›åd35²––ñMêopvGW=töžŸŽ~ÓÁ9GõÕù>³´Ì?ô^áÇ«·²Ê}í wóvñËÞMÞsXVÂ5&ýöü2Äk·Þõ28éœLðÑÑ{ïYùu95·¬lÞS¶ ­L#%%ÅE…ÁnhDSÞÑÖpB“¢¹;@:Óf#$x)|8Ú=±q}žDk˜ë8~ò£½ô_t›'Åëi¤X´Ü©l Ïa‘9A¹vT¨H¾»éä^ò&¯Æ±ã®ûÝ&ðà9;¹ˆmÓBåÜÙÛe‰êƒÄë± ?8 jÛ2òÛYµ©oŠ½“ýWäî×Þ;¦š’¥š*fˆ*ÀA#œhÌñN:rÖh½k–q‰F‰g§KØúòýÓÇb ë‹ìyþˆ‡ò~¿ËØ]¤</^Rµ|ëÓþ#%LF>'û=Þ±ï?W×/Ž½¨è21Š´ôä¯a#%nÏ¥T Ì3«„º]¥„ZÏÕ€g馸хF5Ef)‹Ñ#)*p¦ù·\ ÇÜŽ9â1³k;\¾ú¢tŠÃº'óg#)J·ÿ^úË箇ǩôæóéwqµlê4-u«Ò…qõÜõ§oöïÝÉç<øŒçËÖÞ3RãyïÛÀÅ×rcÁ‚£~ÈÑO{ùñÓatÇúÕÊ7ôÌ i ¤?MÒ^·N©füM=;k¦š†ì§ZO‰QJ##)1£ƒ]Ç@ŸüïP>Tú?jrôB¾ª‘ßÝäfˆ‹½\l´Õ瑱æ"ã÷jð›iã!±y<„C™QýɼÒôßYŒø|”wÆGýÍÇ3@iîÆ39dx4—çðO™˜1£¦’†Akéù?jƒÏçÒc~¬yAi!˜tÇ€å ©D3Š›'““õïßnËØÃz"ö71·\MÍKt²=þxGÌ}ÙË8õ5øâó0˜ÒД 7ré½³2cœ§.%麗 @ç#$ÊvÁþDІ5×´>4ÇQèòÿû“NM´@yÔJ w=Ú`:  Ž%xÛ&Ù¿•¯X”WËôÇ;òktÏ:yĶ5d#%ÄMRôÜó³–ZçšT,ª&€«I$ý9¯¥k ÔIP›w"‰ÄpšH›•H™Ûs#%•ÍÉ-¼¤9ÊÃâä˜z#%z"+e¨Á.·•éK{fý Pgõ±(›,þ°ýïòëb¦¡êX¢Ì;O)##òþ?/ôG½ñŸgèðé¿ó×lø»Bš…y¿ÙíKpŸè›[<Bgyü±¦’*¡Ïû¿Áïñ:ï±°öˆÒØaôJkõýoÆi³"ŠdÊžªOͳöw\þŸÜ>Gõÿ7øŸ0ÜCûËΖƒ†Ì#)òªª°ÿQç`]L ÷ÿhÂÆÅÛ@oÕØ›bq+vòuâs4#%¼Ïñ·pô»Ù? (»s{Ä¢4ÒR$`FÚòOòh\nÛK†Ñè:w=o$¢OT ôlu:Ü¡„Á°Áf³Õb6I.CÑy‡³1G¶I`ÈtÑÜók$à]K¦;kyTUvI·¨º[ŒíÎppå{žƒžñäçÑ~· è½piF2H`ôDAFF ây…Ðà¶gö›çë6ôˆª±ƒÒ©ZªUqÔ†Àu(2íäg&@N0Ï{ôòdi†7l% Äõ~´ýUuþÄ)ÀÁ,Ø”a#$Ã-î;gáåb õ”stëE½ò‡M æZNþePž£òîú†¸aζÖÒ_Ž¸]}ƒ>x¥P‰ªœY#¬éuÉÀa™ˆ0"PPS€á)¤– ÄÐŒF"ª4aF Ì4Xpl¾à2ŽŠg†ì ãí Ü Ì+¬Bö¢Ò›Kì»×À¹±{w»ˆ Ëh3ËŒ†OFU R÷Ô²’T p³ ÌÌG2mwU@ëP¨O—_{÷iËî$)Ñ—OªŠ—»âæµÒÖŸœ#6lñÆït~wuy©c•]zÁÎve@À‘àDÐÉHMЪ¨Z4d©‘bŒ#)³±RjE`Ò2!´Á ó¢†\f§¬#)½‡Ò@)ª#%HBÍU$+ù_ãÿ3âß•™ ´Q~þùºk+",#)ÕQAøQ,¤‡ô;ê$øHv‡¹TA*îÛ³éwÁ‘½ý«ùØöE{s¯sX‹˜ãûz— ø@sôéÐÕV+åÀÖ?ཥŸ ì˜T’àvIà§"ý£:Z6v9jHÆÚDÁ‡+!#ö¯v+mXí⧙uþeßâÊc^Á™)6™®Õ|i%A±ºIÈÄ8#%ªÉðöÖq‡wØZ^¬Ú1Aµ_q=ñ’“Ð%0a„Cñ)ö˜¦I Ö1ˆ1íñãÝmÞÁáí܆Ûør««é ÌÆq)€Øã›Û»mØ.~ãm‚­¢wqaEyJÚ.¬ð3d*Äì,(Ô_Äz¯Bíu4ì⣤ŸÆ¿ÖÈ“$¹‚G~˜C!L,²o!r爐u‹@3sÅ.sw&9)(3®ï#)$]G.ÎYàå4UîHc)Œ$®I©“Íë(è47„¹¹S>¬§mZÕ_+_ƳÏzëw—Èû£Ë$qã9iNŽŸ=tõ×g<Žm¼µf¦I6ÏKÞ¸|ÝAÊÜV[:ÞfqpeãAÈ.´ƒé;ÀädâÚñÒu;öèNí(âˆl{÷màó#%'.R:.ýO˜pi°ÖA¹þŒŽTÏ‚ør†É‘Z†† ž;˯[‘±…„ÎF'Aw!CßüµM;}^Í ºv‡Ì¢uƒÒb–2~8#$#PîTߧ‡^Ý|ý{òá̪(ÏapA* ¢=è––ßÏ^š6¸µ.ú0ÃÎbmè¥U6ä®^.i'¢é.=36ýàÙ õÙH0˜u¸Î&sZv,A"–»œ ° ” ŠYzøÇP‰#%œàEY#$ÅòÚlé2i}"p;•Ã‘Íö:œa¶œ[%›Cß„jŒ+°Ü—7ÙG"&É8#$ j¿aäóó‰;ÐÔØó ”Dó8ÍØL«¿[mâñŒb¤;r°QˆI4Òd°”É"Ï6ó#%±çNž!ÎM'¦3z÷?›0Ì{½½Î¹#¿Ü°y±Cšöa“â p(Tv:n.¤xðÆ {ÎäQdé… J#)•Ì5àÌ­6 »&€qÓõÕEv(ÜS¥ëpSi6›±ãžöêÆíÉ íà¥ÆðŠnIjìxnJlŽ§!R„;SØUÒPÌÍÎ åP=_a±‰ŽÂÄj#% g~¤­®q$wtoTÔ”ØÑåŽ}·ÆôcÛ*ö7B”B$$Z€›¢¸å‡‹Í`ã1É…UmÊe¥ÖéÈÁA‘r‡¿xÄ„ëL#$Ø©‰:#$B@`ÁVXy£¬G"PRA`k4ƶK³.ç„ä±±¦ª5"·QÆòƒccÝõÞo¼û-ʹ܉•Œ™ ÀÅQ¤â­T IÌ£ÌÏNØrÑ,\à@&Ø+ǯ53¢!ÚÌÄ©™—›™™™clÌŠ¬Ì™’J±Û3Ñ/#¢þ­´Ïvt‚8°ÍßV\务 ŒPRAPÀaì‘´ãyÙÁ­-?‡¦ý“VW›<1¸æ½DÆO(£-Ü[žß„-±Ê~yÕQLÓ®²Ñ0DqR±Š.yRÀ¢=.½4õ\ô––[®‹¢s[¡¡iXïÑ<fÁ¬M“32Éð±D‚À˜B™Â8[# ;Ž+=ZÍÇ<0ƒŽ†F™:õBP sÜ®Ýù4¨§ÈBî1bÈ”µI.©lªª¨(ƒgM&þÃßÆôC:íT%{V¤jQaF ÅÄ,Y™ÒKb_NiÖ˧Çn$|n–›|σ=j©°*–4˧p†S54Û‡q˜ŽSE((ƒlcü¶»#) ÿÒ#)!ÎtœuEEZ8t+ÌN[›*„#áÁòÝs.Ïóß´Ô#%@#%#)“½Ø8…Oq~^‘È´Ói!Ç¥¤GÆf±xF )1õtû—¤¶| ׫véǼ'ö¼¶Äç¯Ñ@NäÇ@®„Ø”^¹nj;ù \«•ù·ÂkBŠeƒšÚÂ1Ñ*lˆBd>õª5fɺc}´âo#%o! ¦ä”Ûˆ”ÙàæÖ‰¬@ÚMƒGnÖ9¶<²åÈe‹›‚dս‰î6) ¹ÁF”Q™ÆIÖ Ñ¥£ÅNKÍ£SÁr„”ç¶ì0m#%^ïfPÂg?äý;íì8ˆTB2Ql;DÉ‚!’ä•…öÒt:*“"è‡<3˜)#$™!鹓‰,¶B 2Ì„‚ c M>¶ çi(<¹•#çM˜C—-M5*¤Òp!×CŒ‹©Ö¦u#%lf‰ÛÞJcX_è~¦GÉ4Ï…ÕÍÂ]7ÙfÖLƒBÒ=À1ݱ‡ð ë™6Ÿ(šw‡–#$¤T&#%õJ8^mÜL÷3tLSPbã3Õ©Ø5Æ"LÜÏ D#űNÊ”$Wêm£H)&»`5Æ­!œgdÜ5Øß,+³ˆŸC•i7¦(äÁš3M¦4妵«Ÿr-óãØꌨÁ¡³ ÒòúOsÛ#8y…pAŽ»Jwx¿UÐÐàvì#$ÆHñD‹Te%’ÆÑ4Ùªf$ß3‘‘.ñ)1Ð8É ›œŒ<A‰í:+‘M}™-oÎF¼³@ÞG¨¤·´·=(ƒ%h4Íä o ÙÆkK€äåÑ#%å#)¸*.iÒg°©(,—}9¡Õß,¤|$›coF@/_\l".íÉ_ŸÅ&Cx5tkB.»Ê\÷ûvhWWÖ:5Ç/*ÐÚ’ñ+ùø=õ’q>^2åÌÌF†˜C¿ ÀÆ=C¹¯uÐH 7U’MŒÒÛ¶âT#ÎX ($ý=(Ý‘ö˜1önS^@Èw'Š)õàW«Ð£)«0i)ÅW¹6³m´ÌâÂiŸô4dXjœ Þÿ<0òN~¸£Ÿƒé»¶Ét¨Ž2ò.„èGØEuÿYHBû<cóÑ>–63­ßüèÿqŒdGu‡f3Öø®+Ôõ>Þvn/’ÿÄúcx[­×Ìj¼®¦äÕ«#%ýÙìAé‚…¤¡íól #%/=7wz5…Ëƈ¶#%ár#)XŒ,Ù«`¾´$ÞüÆÃää â#õ@ð QR ¨4Ç”ðàžþâùbŸŸÕ‹1™þ¸}ÿò3ýóòÚ¬Íÿ6{¦¦¹Vï7ÈæESÒT„$mûÀÚª~Å=›]¦h³6H2’#)Ê:úüÝÃÚ<éI#%Ÿ‘Aÿe›‘}G0sæcæ @!#¤LK^wQ&©¦l¥ ˆ¼¹ÐâÂÛ€Ò0€“¡^´SDŸ›ô÷Åï6Ú-ŠÏº—#iQU/×e·Õeööqå !Õ#$¹ØÉ K<âá•è(ï°‡ê>Õ?¢H—`*PÔ#$QÝ”¨“ÈwµÒmŽVYÜOIê²°ÿy$ÓkUDàt‡‚x!LéªLC̯ÙÊH„"F1lK E´WLP÷æyˆH!$'Ý>éršåŒT°"f®ªE@7 ÖÌ@;‘CÓHm¨@„"âk$ Ò¾ý@À*&S,úæÔ£Y!"ZИ¼ó¼ÚdFQ¼µô*¡X‰ ìÅ’i+æj‘ CH3 µ²(Ý#%zûNñ•‰ûì}a‰¬U56o“«‘´’'#ªÖÑ~vÅ]~%Å´R$@èA€ Èé eSí§Ó 2cSÜB¡‚ˆun%"š#$hRÆ9iet2w…[NUÜUÎÜzE rãÇ ì?ˆEóQ2 C¾#$°FçºçE¼DU$@$ Å ‘GÒb;Ho]·äUUýªÚ¬mXªª5¬ÒAh‹‰F#8q~€›’Iè·çQSXDáý¯4Cí‘#%1×m(þÀVVÚåâ’Øj°ÖÒ0h2#$Â#$Æ–G¨b%Jj7øÓ^ȼW¥”’E½îÞ=kr½›{.\Ì5ÝÚI/.î\®kðï<ÛÅÍÒ9ÑÝ\ÜÒVWuŽI>¹ÖKÎC¯kÅÒÐÔ¥Oøsà@˜5" p¯÷NB1`«$BH–,!ïz—°ÀÀH¨{&(\3þYC`#)7t¬:ˆB@’3ˆxN·hø^OO¥þ,>úÛ*ŠŠçŽ¥!Õ#)%F±7ð:îj£Ç=­1c-¤‡Äß7 àü ®™[8V:q.ÂWã€ãššEã$¯Ú,#)ˆb$°|^"„l°Ùþÿ‘kàŸ.MßÝOÑS #$H©œD‹'l%b( ÁŒP"#)GoÃCD™ìâ=>ÛòÝ€ è•ý¥S#¹«5•ç•|å½MLž¾ÿrI¨âðd²q`)N)*çP$ÙÉ®°.Ô¤™"¡$r(im ÷R¦÷3šXR\l“Ëò±UÀ‰ z l©¿‹u.ðþ¬=T~ã –ý!Cµ³û,‡ÕýóžÝ)è§è8Bk\†S\ý_3ku'°û<b~!a·q© ¶­V×}í‚]{‚ýdëßO.× !ó.®jübŠ=îã³.=ß!mô|OpÞÓ‘W'­·~ÙßÃlx'—Så>[o“¬(qôŒþ9˜‘ÛlÌýºBŠ›bsYQ}'Ý~^ŸšˆBUJ©E'_o=ÉÌïYGB ªyé3¥ñn(ó‰  ÐÌQöIøƒ 3!d/E¾qN!;#%ZaØÊ.©VOBOuåÔQŒ…†Ÿª'Ï%Dm‰°eüéÝV³K°ªâ”0‚¦ÿ#%øߟÃÚvý‘²Ê©Q„U2»àMÁáÖ”#%E>ø\.¶1‘`#¶#%m4µŒ’¥[‹M¦ªÌi¶MJتMVThS2­K[Mµ±ç8$œ'Æq·Í*NPá±ÄMñRE€##"$ˆ/G´dFȾ|) €Eœ=àÉ´è[øjâÇk†@(3mQú"pÈU€ðÐËG8“21¡`Êôƒx‚Ø#)²Ê…û¢¨`¬X²1Lãxi ù=¡}²èI)lHÍ䆲‡µ,†ÑÍÞña¹¡¤£"Ð4†´¬À_%a™1&@`˜ªê±Tv*ºh @¯DÐÝtÊ lšUQ mi"ÛE­8±Ä Q5/åŒFO‡rvÀ¾œ ‚UR›tÙ¶˜Ê€My#$X›=úq‚X¤ m¾þ\ÕJfÛ\Û›%r×e3bÉR`¨¨¢´#P© "²/³Þ÷ʾ2DJ€ãXAd‘ÁQÐÚp|zFÖÙä}>¼-šhÛ-!‰ƒ…Â&Ïr+ÑÑ¡a<ž‰"kÄî­NÛ±I?,¤‹ÞèA 9ÙO×Ýþï³çýÿ‡ŸüÿêÙ² ‡žžr½OˆˆÉ ;¼Ëðe⌠L¦*¤ 09FˆŠweB0cñ4B!E0*!…_5S¡ç6á{î;|ùXÅ\ÜšD´#$Ù?ÑdògÑ•SýPùؾÐÙþûTc ~Hx=˜ „¶Þ.jî]Xcî©¿ê¤èçÚWo8É?w03‚ˆ+PT2ãë6|¨% CÕËýùªùŽôêmÜBœÃÛt%(j§eódùµ#%Ou‚ *}–À©9,˜¼ç†yñ tœ&»Èƒ!#%F‰vÆ ·›É9žþ“£ì£¬ˆìÜP"9Î#)À"üÜnãÀj#)×búí‘þ«5&fÌ#nç7§Ò{7¿t'xC±¡Ý›s{é¡é¶{ñ,Ÿ~gSÀ¬4ù8|^+s×÷åÈUQx< ƒò–¤aôwȇ´ùÓ§„ÉXæÞ]û¸¥Ç?'ï§Ǥ†Ç–™¾Ýþd¹Wçþ98ù#$ö IÇËÄ#%`x ¤änÓž!Ä2!f ï:¡`Te0ÚS?$¡q}ç6¹½aÏ·&·±9¶û<i#%¡æV˜:gqNJ¤ôAÃF+úfîÌgTÀÉ#)r2"08O$ ñ‘0±Å¾&6€g·u‹7êCÄ€È+#$ˆ°¤¨¨zGÄ<hï#$N×Ú‚ç’êSÍ#$#%™^0X€ˆÝ¿gÙ°h ‚¹>Ñ÷l|1$QJªŠ…]—è•uGÝÐñ¿˜¤F æTJ¡`,‚‹;ýÿ¾PÌå jTV 'ŽH«¨<:üº8ò˜SŽTØí•°¿ZCÏA"éŒ`}“WÙ `ƒ‡œˆöï¦Úâ@Ø#%ÅÎlÜÌÃ>ph¡E‹Íd( >@áôY2tz<ý•=µú!Ðu=þ•<w‹)½Vcðçø/½Éâèˆr(Ý—ÂÇZº«%*¦@èWÌTyïÂÚ“¥fÐArÚ¯í†í¤Þµ¹ÌC™DÏŸ3Ñxßù¼”aõî/o„Føçcmañ¿‡ôf6+¥þ#%5Òáߢ`âõ¬G­¸éÚ».ž8ëµÏ+‡ßy/¶y1в÷}šFÝm’¡6%Ê;pûgƹë]øÞzmiQ×éí1geÙFMØÊÏKê.½ùãÛRØnyÇ2@ôAțĶ/¦ç[:òm¨4jáÈ/1ñØÕÜlØÀ·é‹cUnù÷ÁI€X+«vPé±f<{– 4)Ø‹HÊ€¤õ#).ƒx¦÷36Pž|Ò›¥Ôx6/•ˆ`Qèhø|19°ëÓyõÜb‚ƒŽ§œ³hÓ[ûŸs{¼Il0ršÖþN8»uÐ2æ:H!Ü£sk!Sâ/´LÙÍóˆ™J ©óX,ø¬ËSRªƒ¸ÐJ»2ë~°‰z'8uÿ-xÛ¨U0ňô?9ºõXlK£<-Æ;ˆHBºï‘³† ¶ÏÕd?¢tDò¾C‘­ß¸†Ã1]Ä|<$kfœ¶lÑrú8íèí¤ê/@_”"ÍÞ\C6Ãr<ö¡ªâtj|=‘~&@8ɱ»uèÝl¥í}͹± AÈRfBèš„ÝÒÉØÖeÙÆÈpÙ½<v7Â9ï©i>½ÆË7åˆ7s¯e[÷9èy*²ƒ&ç%:ò"ÕT ±ˆÄaììñ9mˆ&1,Í¿;¿Dc«T£’Ê¥SÙXžïo×SÐõ"€´zøodÉ‘º˜`•èêdšž9ö½k„71лȘã$–Û|Æ„Ì0²Ž;v¢r]<½õô/[hßøËe0‡±ò÷oÙÚU>œNʾgžúü#)×8ÏÙd ÓÙ¢¤d¡Ê‡)ZRH}Mê©©ôxò‰ÞmÔ±hm:qáÓNJ½JéËàÒï`8®ÅËŽ¹²«ÂÈ#%‡@ý¾›T 2)§Ë†>ž­ËÏ*‰ñ¨uzñk]ʉÈÞ꘽Œè”Á/#j )HS°¥6KWM»'uu,Ñ4[SJ×oÍç-ÜÝw]kÅÊá ®XŠÆÔ,U§A#% ‚QDEÔáÖÝĈตš—ݘát¸¨¥ÂE`‰dsn°ÃjH$‘,„a$"$`Hˆ’&×úgb;Ãóò.ER#™à±dÍ(7M‚´æ·‚ª$Úú¶Ð»A· _—®”‹0#%¤¿BºmDb¾(ª®ËT°ë°RÙ”¡Œ¡î“€!î›çä0;¨ù]ö·L‚éà‡ß$R@AÛœ›c!Fjd °¤“vÖ Ó%šy¤ÑíJ·vÃɸF4ð‚òaZîBqÑjÛ Ão¢Št|kK•#³m÷­{Pivê&fA²¿Vp4À§TKL^À*¯¡çÇ£>‚w0N(?Ÿ't‡é^útU„»¥`%Ê#)  ¢!È”D¤)P9Œ²#aŠ²R[ H%…!cænÒW:¤ÌS¼‡aõ <&_#$5×µø¯Ç2±¢ÜÕcUÊÑ«õ%¼E 5H‡kLB‘1üh¿ŽâzëÀo$„Ç!×:ùë KÚ›>K?[uUìîzpª—äÉ{p >áÞu‹#$«—/¨·‰øâ‡'…¹2ÒÓ-5‚ç–ýÇÄAñChi¡%óqè)çÂòÑJý¾â–7"<ÃÒÈþ $—b±áäA²@'ù^¤ÔÓ3e#>ðÍj‘ăs&æ…[ä‰L~çýû ÃÖa–L¹ôûü»N}/kñ‚2Üa±Tqº3¦À*ë#$Y…TÔŽP¡=öË‘Õ ‰%bÄPH¡Õ(DY9žS¤uW­_+hwtÜ£PÎú½ùH@bzRÖš”’l†ú…SXžbZ‹ü#% !·[*®æ‚eU¸ïÐœì÷' ÏJÛ‰Àá[ºj‚zÔ¥ÏhÎËOi)œ>̾TLNÀ Ýå nJÒ5'º8 _;ÊšËmËÂm£Ó»Lœ™ÓñÇl2Ãu(8“Ÿƒƒ)$’¨>›xLÄÖõx2#$Æ»;"Öá „Ú]ÃeŸB ÐØ]B FÐ/×Ä@l#¤ØT&Áì#%@p Í(Îós¹,Ô#%FAè@P€×D$%Î^’à ƒÁ K dN/ó¹½ÙJý™ñ¡Ë3Á#%Â%ÂL0Êåy“+H½B¥E@¨ŠÔ]zJÉMAàÁm#)ÜBÖ#8[]-yK­kß|«Çe{2—&(@¸³ÔÒÖˆá="^^8 c$)i/°xÚ\5€ Î)VK+u#I¨†Sa«}ë«­µäšŒ,êR“¦Ë¥AåúùÎx¾Ùá+ßPü0?’$oÑ6Ùƒ&âpl5„ˆèÇ£¾±.¶ŸX‘×#)‰²ôµ3~I½™R0¢„x w'#%\øqO£öžA 2j”ŠJg#%OWe7ÆÄò9S=ÅÜK× lXÛÃL͇½Å6#׈é²D–*·•6r=øà– äcog‡‡«µH¸/'™¿XXyõ‹ex_ÓÂF£N]M‰Rw,~û›×ŽÀÖˆBå æQI$ªhvÏGqýI’Ed ÃE:UN•+aîòú5wÞ  +òrÀ©aÌpN¸9†:„¦%,{"¢é 2ûÎ=âó ’ Ï –)8˜|*^#)c˜>½G;þŒO#%$¦±…Â1$1#%Œ60P?k jF6Ñ’#1à !>á…ÖQœlV³D‚¸`B-Š^š!‹uK®þû=L„!tX•*Øv`%âÁ¦(ê‘Þð9ºó:ù7‹ÌbU ªö«xæVç²@; ‰hLnUjÚ­ ô0`‰ånÐÿ)Çi‰q“HÝT x<´-šºÛ+ÚØ :Rp„žÕX?„ª©°¢d×U’ ^:ÝV­6©f@ç4Â>.dLÄ o›©Ãi7Hí_8àml6ÖQÇÃÇ&ÎùÃ"TcñÉ´f1W"J‚òŘFñ@P²¥tQ÷Ô-ïHn¢6ÿUè‹ý)!åŠè…»mw©åf9 ±†š–:ëYÈ#%ŽÈΊ9V#%6)¤þ͘Œz5 Qb¤"ƒ¥·¾Ï,テ >·C¶{Û^OZÊWׯE f FUíD HÉÚ„`±ªÒý¬Œ’ xݨÁ‰¨ÛYQ²Ŧ‹^üwÆwa}3îœc £)äÀ¬­R ±è§Lli‚Äxp5#k³–$½Z%}J#%Œ_ôoMvçgL·P–+…8—õC=÷n aaÈ™P@zæÄ¥µfs圶ر¶Úä€ÍúSÊ•®ûL¼q¼NQ×;o“ƒ«»#„„»IG¹(¸Ã_ïÚÿC^aœB#%yr†Þ•Y΃N%â–‹´‚Øi‰ I ”Àd"THˆAƒ®áoJÅŽ%‹å— YG!#)(Ò#`7ÀN¶OÁDƒci¥ÈÀ6¥*î>Ì4s8CEb2CMl†¯1„)#$ÈÂj ¹b9L݇Š6RC97¶#)Ç0^Y°Ò‘*%QPM0–†D)„±T2Þõ$(b‘a“ƒVê9vˆÌ¯îƒä›Ëj†‡Hçø™ƒ2J9Ë´$šôûÆ(É­°Ò¯mA3…PNaåcZèL‰¸ÇSŠ)ô‹Ììÿz'˜RBpy?3éO!o4¼;Ha†$9à-9†™ÞkCr™Œ£!W®7óåAÉe†0Xá„—¦ ¡Â#$÷”†¦eœN1½éC Ÿ¦‘8@¶¦®eÅÐ3ažxì¤ß? B:ÿM31þþ¼ÙöĺVõ3Ê"¥¼C @ãT¦fœ;{\æ ]7(áhC;Í´,w%î”'‚JnÙk=æZŒ¾?<7°¸jgËÉ÷>v6Ñð?2sZ'—Ø÷7çÛT­Ì­«tjL;ÀeBÌ]3€úÔÛg\Ë9¦Bú‡ÔbXŽ½7@øCàÞ½¡ uüx8ǘ¯|¼#%H#%Œ@0€ö›>Ž’v£çØgm5,u´PÈe1ÅDÐEƒžK@Lgá凜À EÍÜÁ¹J#%’ës14ÙÛ$‘ÌM4hCJ€#%#)š]u#A`}í;3@Se€&#$JyÌjعPº©˜¥AD7† L S#%ºrn8ÐE ¼Ð+<KŽHÒLíôÖBÎgÁ”c9ÇŽ1eûáä‹$ˆ$^2z3ûiµågš86Mö\žÍT¦òXËÍÅÑÌ¿Úy*#ƒð¸L´VÅNd÷ѤÃ)³¤ÉfeÅe`Îkn§öûb“##$‘PÓ²ÂpÑ<Ôö(Ÿ4Y$! RÑ[~…ÏÜI`m¢ªkÅ·6Õ±­µF­\µº(È‚’Ñ…#%°ù´{<»LæH@Î}C(oDð_QÎô#$6¥” TOÆ,¯G 2³ÄÈóžóŠB#)Q# F„JdÊYµ3L’™4Å&Ø´™P~Û©35i ´Ò¥ ”Ø”)5¦#_onPÉ­&,J%3k4)“)™#2#)4"¨¦%2Ãìî¢#%…#)D‘I2Y@jŒÉ‚Ѳ£E‚)„Q˜ÆLÓÛBeA“0Ë¥Fb#%((HÔÅ4R7ÏË“ÂìíØî¤GÈ™‚m@™¹øÍ­}æê'‡‡Ýž&d?°¦Íì:¥¡*¡)• ñ8yLUdXfbZTt匆§KhÃ~ÛdA0$™E´ô•Ã¡$Æ°wó5½è]æÑD$ü(<ŸÂƒ&íÌÝ…ÂÖî7+âä`³–Šm^»›3´$«”þ×¢8®(N}™˜Ò“÷“­‹Ÿ³çä×Ì/¶åáÃÐ6Øc†µŒt‰%†Àßâ*½ú-¿~E‰Cj4ÙY+J1I´Ë0 A‡3{Ê‹xU/wDS¶SO¹§úÌ<„χŠ¬°øóÂ1IL’¾Xå´ÕIÈ ¶Ë¿fþŠáµ¾Rö:…“Ññ)‡°‡U#,'k=?J||îåüÇ=N‚Á’áT‡:¢Ã+˜_#´1h3•è÷Ù¹uMé—VxSrÔ–0=vâ|[ê^f´BvXmZ¤j„‹D€©#"@0íc­áÓåP NRi§Øà|Všc®.³2¹â×8‡&¡ÓBîïÇDÄb;Æå·ÛÍçl;ôw‡b¥–w³éòÿJ+ÃMœbMhjè(I-·}½zÁ"2¶Æ”°¨"X(EÉQ`»ísn0f'¤f¸½¾š{é·§"—^eÈÖß“}¦½,3O²aÏã}èç6äô½fE‚éA.ˆ(_Žº~šïÂ÷¾nŸ¼'ÞŸ:—Û4TÆ4‡å§0ÜQ^`ÛÉ.„“n&Öƒ]J2š¹~T#)¥=O–ù®H{ß'IB·§!0Èò¿ ŒÛ Cò¯²YÐß2qª¢í §€ÆR{-PZ eµÞçeÝ#%•}Jvìm¼ –Û‘kC߶’ &Ôy‡Jf8#$Ÿ£Ó7Ø@ý=é<Ûy÷7gwB½ÊH C–ï:Ê·Ï£ ŽEã¨Xlì=<B pß.Õ¹Î÷ Xœ¤‘Ú‚»" mña¢ {l¤CÅSÉŠ€:ÀŠÇ¥D±…Á9mÆO6]Nx¦`•§‰R{z¼-è•DªYz#)Œ)¬ÃÒšø3xï#%m³®Í%|v;@È]À’#)Žn¸Œa,å¢ã‚“™ŽÓ»ºxù¾¡˜èajâ;|tíEtëù ÀÕ„ì+©· ò¯óCÔÝ9QúÉ}Õ_uÝžÖÛªƒG×Bi‘'ÖÑGjG ‘ìÌfjVprÕ¡ë£#¯$ŒoS3Z?ŽZ·ªçÛÃ6˸ÞÃ1»Î®™«›†XmœŽ×‚É 0Î&šeXYxqS4µš¥jPÃ1³ a™“¬“0¦Ö¦iš#%=›»Æ¬*Ð9Ç虚%rëÈÛÂÝå0'œ3kUó‚þ;®ü#$"ºÊ‡É ™ŒÕ‹­ñòò%‹"î¦[CÕ“2Ö˜ëµciª[=Ú•ñ»)ÞJÎoIÿaÓÑõ:¦ÊÝ8ƒ½8âáÇqÜ~^¥àÎÑ…ÆÆŒcW´m‚Fg*3#$gõ¸Za™C¶ècP>˜]ªaë}‰eÌTm…“‘0p \±•%¼ÁbQ•¡ó¡‰ÝÙÛO‘¢#%³Á½ ƒ¯0ùm4#%fágDÕ[ê6S’5n°Ò#)qçÆyÒkÕ™wB¬ufB¿+P™ 2h˜µ28Í›{#'[ë‹2úuiã&>8ÎØß@ðc—»lÉAÆgZã5OKK=§ç1Hf2ÒäŠû¹7¸!2é{틇½Î÷*FÁÚYÞ#ÙÕF–®²PâúSc]>Çu)ÔáQÆ€ÂMd}ýcçŸü&ÕÍë}Aßj!àv79ÓŠó‰ÄI•ÎvÁ—yZºÎª3£8¼bº¾jwÕl)$™íÙ¡C¡Í¶ÂªÜßlÞøŠ=5œg-÷ Ûbä‘Åž·’tõx’Ö4äNѨÇ7ÒY,mxHˆYëÄÇVÃeÂ7Š¨ª8þU¶áåêí½baÝävß$éöàÓlg0iŠ3÷q¶ÎíˆÑC툮)á뺔îôg3OÖ)3¦0’jc­Š† )E.äÍ [2„ÖI7;-s•’ZØnuÇXJ4“Òá¡êRºZˆàÓ ª0¡pÃ"õWª^|¨I"ÜßP`QèiªŽZ‡4ÝÌ“X8¥ûåÒê3…‡)¤d+z|Anš¸Šˆ«ÁX9}¢öLZl9”$ –1f5W<s’e¸ÒA\¼E:Ë)‡ŠfxÅ< ^To'a™w´’qIf›lëÑY¶YÊ`¬½F:ÈU ‘ÂãyÞëè¦K•¹µ•±³ÁÆ ¢WèG(å0ë‘Sðk4ä±²”מ¦ÕG8l¸ ØD# ,òCìí %Èíƒ~€âFšw̧]`€ÕÍļ&©·«WseS².k£ÀÍ‹ÉϽåQxâVhlñ]çg6u#%"Æví3Í®L²j6)e§4HlúipXlhyz壜½g)¶.[Mf0ÕÝL#o#%ÞÁä^`êk‹Lò=Bàst±h“4”uÓró„Ãr¦Dm¢.ýkr4Ç-›zÑ‚Pé”tÕ"0;½øÚÌý¹ö*.`WNŒk'ñ÷SÅèqΘŠšt’ EÉ0:UgáoE2¸v¼oO{†Jkqã1¶åÎ2ÃUŒm·ÂsVߧs2Í]7Û‚0•š°¸N$çSlê‘ìfo,Kݸúg:Õc+Ø5¼ˆŒ§’Ó$C×mW³[s&\Øõªcp}´vGC÷™5%oZo4crÙY©hëdöê¼|3Ž¸ÌáFW•¹póÌ©ˆ·ª;Áƶ}h¥„ƒÍYZŠNûTäø‹Ã¢¯Íêéßzc(±i¦,KnéKjh–ÅX#%bìpÍY©€Ë-M¸Ü·Ô´+£ÑIÃ$8ã57V#o‡§ŽRL©²±³8º×cX÷§#-š»Ê马ycÉUc2ÄòL ÝZ™­×[Ñ.¥*b†µJZh=²²Ãv#)sår®_¾@aß ÂΚ¦õ4l®\zϺb5æM‡Mb~!³:¼DJl†ü©vórdž2Q4úA&nÞåÔÎבÝA½•ñÙò3‹u" Ï5/kb¦ÈE9¯#$Åš™©ÊÀÒðÛU¤º9£Q–Nu1hº·¾©-Xlì¦<Æ<¯¼zcg·95§Æ4N" d,ãú’qœ(ÝV¶—jºã³X¡]ëBá$šŽÁnó€Zè…IÉÑó]5¢ú™9 ÖPdI¾èBPR½/KjãuÒ<–ž'Qn Ã»‹G¾ (˜€ðÅɤc†‹å‚‚ïÁ²‘Q±‹Ìá ÌP5ŽÁmÉ·ßEct„6 oàÍÄ'#%ēɇn2Ø[!|¨¤P,È£ŒU™ƒÆ,x€a3Û–šÕ'oJ#%P4y°§ƒÁãH`¹6 ¶H°ÜaF…¶JMª–#)éYf (Yb-f­›0§“I†S[½Õû7Il†WG))"Š—l£ÑPŠ¡Á¹DiÛH”1i¢Ý™­#%åp›)™K¢CÜæø ñ½rdNÏÈàØÔÊä~°au—…Ä.¸¼D‹LiM+ÙÂ&pL*€º2ƒ8ØΣ=s.S6¸²Æ4›N„É©±‡»:ÁçpçžtŒ™} qП”<>ë¹ÒN%q¦ç[´²ã,fuŠÐÆ_g¾y­æÇ9iX–Œbˆd>†jHx!´d}&›NôøY0+4 r÷ëGS[ë}¹YÑ,‚îŠÃ#%5™F°gx9ºšhÌ6u ™Æ3ÀÔã°F¬=Ù0ÄÖÜb(Ó{eb¡ÀŽUEVøEUÉNS!tŠ-œ2±€,|¦Lˆ,›Ažžaµ»·›å¾ŠÆø\Ñ\µâÛÙµsE0ɦ]`RàRS8à)æjI¹ÀäJ†‡#)†€-£‘É8s½>ýq eʱÖ4µ’ÿËPŒP"m4˜ù#-æaqvî­·ˆôHľñ¹ÑéäÚGTF†P‡CT6p–Ä@(Ìá‡#%á‚ hqßyL…àj1Y‰¥ëØÞ@5GÕ‰zX»BVò©HˆurŽ˜dDŒrºIÄé5 N<ʇ@èk©±L/’0âSEK—í5*$Œ`"`Öm5:Qž¼zˆ²‚T–”è)•Ì åÛ•4@›!¦[€fDØZo.˜&ôı#%Ra¡ƒž¨pkU(FÚÐ.H î5êÞ¢äÚ#)»¬8F¦Ð2.:Ža³0s˜:0°h”ŽbebXß{KÔw±˜mšÚ¸72#)fb]ÖÄg8P©,Ôð#$!À¨6weM¬™Œ™«5UýäµÕ«ùJ_â$‰*@17Ñ`&¥)„SÒ” 0ÿ.ŸÖqWêðosˆˆj_›mùî×EXÈÀÚ–©i#%ÇÐ?¼À1$êEu@ Й¶o;k¿­«ñ®”Ö¾¦W[ªìLi1­æ’K@Pt’Tƒ»-/èô†Éêìè~¶±3åNÙT2Ã>”w‰Ëd‡iÁÙG>kŠŒ'7mD‰ qÈ©k”[ŸPï#Slað ·h©b®ÇF⥥͎IiÅÓRk6•£kvÙ°AlÔÀPÖ ÐÒ!2L÷;ýŽ­°[,rCÃp„"ÁŽÌo¯PÏÀ<56–Ðýò€¨¼Ð—8Ér…"H$c¹Núõ¡TVãM†OÙd´ÄNØB $#r""P,f6áæˆÐØ9$ƒ¨—¤0ø{Ñ¡—»‚µ¡Tcç=D¢°ip‡·ÛÞŠb‡pûày@n‡†!Ö+óQUoÛ´H0G‹ HPWwwk³ºî̹ºéqcmÒþ¸Y ø» sQûMO<Ñ˨yãp#$°þ³˜D7I)›PS‚¡½€ñ?F#)"ô#$Â1Žtƒ˜ûˆ¼xõXdâE«PQ€Ž_qí¤R»J=¥Ÿ— 5.þ'ó™#)—TÚâý-…õàlÿYï÷°Ú<ñ0¬&MMvfÕ´Ç}‚ƒÁ’ĦdDìíCi"4š]²…ä|r ÍÁiM^̓\±Äp¤ Ðj$§ AF=#)àØ(ðÖ‘ÊÞѬ'hŽbäÁbF#$Œx~qçË\’‹;@J!¨ÑÚ~ r#$ýäSPù«åñ -[TÈÞIs#SÎzúýå~²Ö¥¡%¬mŠ5hÚlc ±šfT•¤Qd„#%2$B”ÂÑ> »Ô`xçYª|»D·RžÍ½]ëŠb¯èêvÅCéÈáê|ß(% Âè±n6ˆ,–"11Ž³XÞZ V£´‰`v´ B܃æÞ½-¬Âü)ÝE¯­ÚOå»'yw;J{Se2†OÛtZPS#Dµ~@íÓhŒ“¬I³¿—'*ÈVa’`AöÕE3Pj¹3tP[þ#Š‚°ÅŽ´"È£ÜB9zÖ®UÒ·Ö·²õoDm™J´dÆ-™JY±L¸Â´¸fh„hÆ#Ÿ¼ã)\KDˆs›ã#)Ÿž·nÄ#)wªbF0pѪ¦µA’i0`ë†^­a¬pÌÇÞCÓW{Ä´¡‹ž&A7¶¨·š^Î57šL¶íÇÀ».]¡¶A¨ã*¯%;joE+]ÝLÇ%‰Ü\"µ׉£¬†þ„`Kmf 1äÎ"ãÊŽÑ1¤Ò¯`ºD²b+®—ùç½îBx§„NӻĦä0cMŒe`JD˜S ˆ«î#%$ºà]iûbÚÊQXKÀßglrƒLÎ),1Çð€‡¯/*«UZzÄÌÖjOŽ"‰ƒl I&„`Òð&RWáP6šµMêl!y¬êjZÂ’¦kïr0µ´ëíq&¡8'7[‘§ËÛ¥ŒíIðφ%5†ì´Xh4Å›µ7¢±A”2—ª2˜°kh¶––v‡„OðØ%¤äÎØ"WUè™Ôéc=ÎuÁKNl°‡ns׊š5’š–ô`<ppù’2ñ9‹nŸsVZbÓ‹ýøN,‡[1ºmÇ=N $PŽ¦Æ«iS‚4ÙÄÁ™œ,”ïœ åðj.-£D"ehåd9È›U7E4¥šÃZç<+†¸AÆ xÔÔkGk"ju´Ú,ë–r· Ð„ û1A!,aAÑ!#).Ù#eŒBŠ1Û«±†!À#%\ÁK#%#$p[!##)aTÃ#)`¹”@Ì°8_zj– € I HY¹Â36q¹–€žýošŠÐ?Ä@¤ˆ»Åvn .^žÏ=9T%þÜÉŸz¹M8¤Qb{(=GùºPaù ë«#%®yøeCb¾4v6Épì#)ØËíµ¼×†€(ôÈ0‡á­Wœö¦&„³Põ™õ²EÉ $Œ‘Š/$ŠÃýV#$´mdª±jÒjÕ~Ų¤’$!CÜH|âYmq•U–èð™LK‘ùŸ õEJ¾ÃûßT¾œ_¾%’ÂÍZþø6óDëªwŽ«Fö¹”Æ«¹6Mìé§#%gy“LÉ•sS#£*T»§–Z̧XUŠl¦Â&Èd­±v5-YÖ®”¢&†ClC8MÒ.™{¼æmÁ鮊£’ÝJ™“I)Ò˜JQ‡§—jx+~—K\‘¢LSI”QåÃRlˆ›™.‡Ð8¤Føáæ¨D]Ê4c:µ:ßcŒuhÖ꼧¥sù–ÐØ¿ÌäÉÆòmN·äLÌ[űºÁH¤;,+§ÌR)ègC1ÒuĘ•pìÎݵfgiÕ·Šªo|V6Þ¦Ó\$×3’¾fŸl¥9¯¼¾1¶\d“%aÌdž9YàhQ´î›&ßg¬œžk“SÅÀ£D1ægîa`ø¡`»Ea¹9˜¾DzÙºR¡Aa%D" ÅŒDqX0õãì3„-fÖ°Ø9r¾ëJãÝâ¹^e5½—žW²­yyÑFºÜÖÓ­Ú³M-"·3 9%РJq‹"í†Y#)³±Ia†á×ÿ +8ô4Ã*ˆ•ª¾˜ù\º¶í c³tÛ9Öös®]L»Žºë‰×qKÊ&·Ji²u«´šÁ¹]×nî)4ZóºQ2Ó´÷uÞe¯![Œ¶ånÓQhÒU,ʳ<T‰¢›FÍH#%$‚Œ¦´Íd”™eclZš‘L×Sk¥ie-2kJeFZ¨Ó쾿<&¢¬Ê ™¶­ Qµš‰ Œ&òÞ¿1“È\ÔyÂÚhLÔ:˜€‡ÀØ@“ì²PD´MÆöÑ‚—bQ”T¨*¤ƒÇ!¥7Á|ÐMÄCÎvž»£,' „aØõiúR÷– 6S0Ì×Ømú2Z£ï"h;ÝíQ! IÝø [0‚ÀZM&p2ð‚Xwç¦f6¦·è4u=F-%#)6=S½æ÷§L’G€3ãÔÄ70ϲ[”àÇaúŸ¾Ê¯To^‘$V¿#)-êD.\ ƒo†fPV’Û9¼n©å”kÙŒ&E39þ™çPGña² ±¶Ÿ”]"X+©²Lãu²R“ª¸ZÔÊdWÂr$#)‚‚f‰7áÓãüœ²tv°í#À6ðàe }z.ºtvƒ¹“˜ü¤ôàÞûp^h½æ€RZ+V»KÄnQú¹˜,æÈ^q`Zd!æ €ckb$—fw?Cd6B]«{ƒìP¹«ààL7¯¹Hg\-˜ŽUmÿmá1£ÐÄ)ó);~‡wn°qc³`R×Ox?J?®@#)¤U"¬(øz>D+ öO3VñtNê²Ê(÷ß Š5õ€Ä¿ÙÅM *#)¤#$¿<È´LPPÉj""‹hÓ4J“ÞÝTD’Mã[\Ðf¢VIŠÞn«\šm6‰Ki6b©4Ë&šd¥c1M5 Leù[¬2Õ”ÈlÔ24Ä–Š6Õ+TÑ{ÜQ´ÖÊîéjïVë]«ã·bŠ˜Œ2ÛS$µ4Ú¢¬A©£6ßЭ·Uö9 $ÙXÙ+kl’DÛ5¶¾WjZ4f©&¦¬yçxf’Zm6Q™SVV[+|;kl¶Æ#Dx·E¦ËyÕ×É)U1B¹]YªMàºËx×TdÄÖК׀–(D +îpåø8«•5æ{q¥B°CF;÷p–#$ýege ÇvGËÜuJóiÓ®ñ6û¤ÐLct'X²L!·zú·E¸h%gÇĶZÑ‘ª êIh«”ç åõðÑ&­ð¾U­o›m>{¦jZZYš[EjRš©_g—_ä~ŽµùÛål„F¬Ò¶R­ð…h‚Œ‰¾S‰ 4•!Quª5å*ºlUcnXª5mwßí¥°`tª&tÙM¥#)&*DSMM\Õ¶,•ilÔlÅ,fdµ~;JèÂQ©IjSj´Û)–²VÍ)¢•&¯­nPÉ%¨Öm‰h&)F”¢hm6ÊSHš“4YL³E±Q•²ŠXÙ2&‘¡FÉVJ©JÁQR›*J&Ù6¢“l†,‘¶Q5F¬ÚiR$É‹$¤Âi’i’ŠYª›c¢J²$mE,Lµ“²«RÊÉ&”É©Kl³j¤­ªújµÝjÒ³kM¤³i,–úJÕtÙ³Z¥-­%¶¨µ|µy³T[y›m¶²–Å|*®kjD”TE I%ÿ4ò#%Üᆽä.˜»°µíx:’¶½°†[2wë2íuÃÇ@ý#%`=S»Uá„¢fxbn*Èl""w·m NºT_yí#%Ïw²QÆ„=ÞTÝ0Q>X8y¯Ü"³ #í‰åõá€yC|vGªX÷· rY¯Ïã»êÙ×]ضh ÛYA3’ìõ]•Mù”£v”<<‚*R»LK¥òK>SËŽ¡רhÃß›ìÁÙ×t¡_¼ÔbȲ,ÒÆ6†ÈhHôž‘ÌdqÙ$²ú^@À¥7ÍíÏתüSñ#$x,¯ÐZÁ<-ÑA`}@/‘©³\#%Ôs½l#%°L>Œñä× ý9*`ˆ¥¿7B SÇ>0:³)ø!ˆ¸ÊYeiúß?ÞØ¢E™ ‹1š"„$:|1z÷ä³<õ°Ç¡¾;3˳kÃËv;oªÞ»§¸¬C'~M=xæ#%&ýÝø5WÇÇݲÖñg¨D´Œ_J‡ÕRÀ`ü(Ñ-iû¥[¨c¥ ÷í8m²@¬’3¦õRiP:Ù;$$¿¤H­Ýú:ª9¶…¹MFmiœû#)£#%h©¡#% ËÚ—Îï(FÂí" Öæ«ú>—Öù÷mñÚpb¤]À#$±¶ÆªŒßj®ëªä=œ>[îÖµw˜ÎÌTA÷Hø{`ºS«Iî“Û’’¦ Ÿ7ÑÇ.g¾ÂHƒ#$Y6<JQéà(Ón‰H8†uì#%°×xž÷áöŽ$ÖÚõéZŸ3|§ùz6möŠ#$yfK¦ÿ»µt8uyÝ‹ëp<ÕÕß#%± ³ûPÑ9‘Eqȳ•¬œç\ 3€·OîþWªëºéÑW7"þ¾y'ñÞ|;s^¬F”+Ίºhó»å¿¢o\ßa&P¨’ŒkrÈÀFa… ¨ÁV*ô4 É&j¢Àm&1íÖ‚ ,¡5#S 4?Ü£ËjZ@ÍÓ‰0ç"­²2 vÚHFsba‘#%‰«+Ì”fGM±ªÑkDª¨,Š×K¤& ÐðH°Â¸ý†ø¥[ô©5b0k ±Òôï £Âg+Ѥ£À$}ê›le#$Òб™èÈì#$q#%‘ïC~ú Ì‹D0•¡1¦ª&*Å\,vÜøÀM°HbІ}±çÏVú}kͤìÀ™3`µÙÚ¤áɧoFܲKé/Ûbñoe¨ @AVÅBúf‡WI}Ú‡ñíVÐ0†¬î–"CÞÑó7(p€#$MŒ8‹*²üÌ-@÷æîòœù,`I$c)Ó4 mýÃð˜0ƒˆ«àPù¤I#)\‡\iª¦ùåç6…zm¨þN·‹¶ NlÓx e¨k/´œXgÏcªH¹P!Vúñä ¾yðûð÷îcÂfô#áÏØ©#)g3ÕéY›h–½VIƒ·À˜2õø;3l’vÓú‰cI?\(Á7zôï>(a™®„3U Õ‚ÂR È_Ó.N z³ÉÔ S#M)L7E– É#±!b„µ#$Ø4Õ;Ô{õY#%#%Âj"…ŽAQƒ2Z=ó8#%ö§kN¬ôë­UÈfTéÖ-Â!k–FÉ#)/jPxG"¹ÖQækX#%§£13Õ®ÔìH¡fŸ<Ðä©!aDáøQT$DMšæàÜßJÐíSõ¿dUA©çÄ1í³˜¨¤ùµ  ÏÄP)ç6åç3Þ¦}dA0I¡x6UÐ[ÝB¬SIDÐã¤Åõ 2­&‚¨1Ú‘m.Ïæ¾òáM¯yIeèkA—#)ò##$®|~‚6€CÀ¥46{Î$Q4†I´XW'»Š±5‹Oßçp¼ém<Zq«ŸÖj#F¢/…2h†7ÛCVˆíÈlùò5N‡;s÷bݺ28srYŒPÈá_NÈ™Újè5îËU†¡´Þ¶#$Lˆ`#$àwøÔ“—5V+ꢕx=eê,Èõw3»¬ÞVÌóT.4÷OLœC|‡3 .¥ØÆèÖæf¥ÚÔí]v®øìpEð]á‡ÈíM-H“ÀÀÊåŒÈªâż²Æ>õGvÒEhïTÙÐm(‰rI#)N##$($'N%”‘d‚ÅuªbÆbŠîC¨Ýw}låÇŧ 1½î>k£cVBI‰­6‡¬;©Ë·cgeÈr_U×]Ü‚_SKó÷ÒxÕ¢REM*u&ÌÀÁA¤•Y¥Ã½îIB&ní:á!GuàêèµÉJ\éh9<xÏà7¹ <®®æU9Ýùß-ç±ÞëºTF"pª?UC)l3@7–ò“õF#)Õñ0±áÄÅâžOZèwg¥áÊKQâùRÚ«­ðËröˆöÛl7cÝ99öV{Mì'#h6—Dk»\xÈØKã}ÃÆVÙ‘ÉÂ,‰°îÁ,~ÏRa=9‡#$8'#%¾>|ºDª#rø‡Âó£»Ù‘Ò“¹êÚ'W¸ÈCP=x"p/ŒaŽ(Ø0  ;ðQíER]SY2s9õñFm&wT0eÄâ\´®F zž,EIiïmEÝ×I_]‘!”HNóÊøÕ{«´›µmµô»kz‹ckWžÏå<];«©’®\Å/„ËÌÖóM×–æ­²Ë(F(k‰‰˜0> ò.CoVZ®¼3!ñúvñÎ* ÅC±#%#%,Iéá9õgr¥Æšá»«"vP1AÒ¹“oÓ=^ÒÌ‚ÍÁëêòèkp¯խ:4jȸoûìÁXsko‰¾“Êü­­½TVú6iŠÄ•U\ŒÛ,Òi²ºè¹PÎ릕ØÛ~vÛ‘"´V½¥ÛÕêömkÆå©fßÊþOxµ’µ¦Ù­­/R]϶óYp®h“ Úþ|Sše>üia2뵙ĆP- ²—C€ËN&3¦ÚéŠ{àb‘Éç{…Ìžƒ\Iã6Y-U€tÁX‡`—h‰‡÷cïÁ·dfqаj,#êÞË`ï7›Î#ë’Š†j¯÷wê-)C–ú*Ž’@<ÉUGÍTª2ÕSÒŠ#)@œÂæ—#)rVl5dóTy¸}ÞTbŸ§mU5¨¤H•E8´ÈÔi+bÓ¶­º«\>m{I±FWU— a¶V™3‹$æG›‘î¨p8mç#$ˆ$9Ä3 ¬"[ËÛ}ç¤&ݤß)Tå © ü’’"s,XÚSÈ2O¿oq‰–^}?õ°ÀEÚ經`d Œ–™ =ß?¸åœ’B#$#$¤#$J?Ë@oû-Š¸›úìû-Ý,gÒs&É!È„‚. óò4÷lÖ&&’/‰£‡ñây“å¤ú²Ýk]èŽ_÷Zù¢P3Jˆã´3檆üelXrÌ1¸y§ÜࢬวðîøˆàE@ôâcé¬8‡Pë*Å]¡C×9ãâd묆ýÝ!y´Ù¿ò|¯gPïh"3ï ‚ps6)–øSTSb/9–>å~3Ð…þ3¸‡_œCÌA$N ¤ªF¢#)AUE„#)Š•T†HïFŘ‚TP‘dH¤šHT¤Î¨z_qQ°»»ÑýAWµÅ[bêT2€b:‰B”gã#$ÜÈ"bvœÓ™ôØõ=j~O·hrا#)òSóBA‰¹<éëJ4zBrN&¨ò³‰Xÿ8œ9‡DŠ‚’#)@ì„;lí-î0ÇÜ’Õ"ËaH¨«$+ì;¾¶y'Rf³LŒ&8÷vi¬Ðh%èr–X%nxs‰tP?ié=S”k¿Wq@GP̳1^Ò€#)CÜ:»øž‘ûÖ0ˆwP;1a9ªFdüãSïæ #)~©TX&õ"ŸÂÃö5¦hþEû5©†:T‹BªšíÅ4T2H’©IK#$Icº™!—íCF,Š:×ü¦30è¨N-6 £D6rÅâÄ&Ö1NáuËÖ‰0Š±c›ãƒ @Ùh)/“͸ïz.´(h…2KH´PO¨4ÆÅ\Vi1Ò#%Œ j¬±20Ì®lV5#ÓÆ6«Cm;µ¹j6cë[ϧjõmoDKn47x‹bbo†SQ#]AÛ_-­Í· øo&«ÆÏŽ×΢A’b(Æìæœ0B ÎQ‹LÈè––°[(- :”n™W5š\¸Á¶EXVÖ¢ÄÞªÖE&]#%TË-8Ua,–"‹XÓH)±sj(Ù6åÍsQX×1&5çÏzôÈöëµ¾¢éo=ís8“;H-3†­:‚­ K¨2.ÿñõ˜i–4ß%Ã#šMeGR#%éøø|¢ßjù¼âð\XònÝÚêua¤Ä´œ’S›:] ¢¦´RIÆZ±3͸´ö>€dy°áàLÌßò¸ù€ù©m²O5I‚kªnt!cê{ä;âÑ3øÍÚ¦fÍ$ZɬƼv¶ªÔÒX¡‰ÓèÄø_ù¿¡ÏÉs·¾{Êlœ‘8#Ð4#) žØþ¡ù§ÙˆX„R5pEé#4ÿ4}ä­iÜTny[&¦ÌdÔY¶í¶2WW$ÉQ-Œ4V(¹«n¥cV1m¹»[ ‘ 1#) 0Fhš «#$bv:`nj ”T£Œ ý\3ôÏÂÉ¿ï@tngËðôñõ÷ciçMZ,`nqùý&kŠž:"HU¢©GöÑAhÁZb5LƒKº‹,€†K$"Q" k®¾¾ºl4JŠ:qêökMÛÅ#$ œ”ÜÄþ1ƒxª¨$ º«šMëW —ÔóM¬–ó®¦l³mÍsmâ×àV¼—®Ôlm“`Ö°Ù#%¶:V6+ªîØ«Ö #)½‰!zb ¤T¤‚¶j‚B4ÄÂ’kRmT™¦¯ž–±$4T(Aº$—(<ÉŠq®ó2±û9uC©¢àE­¬MÁgr£—$ŠHØWå5E˜2Càê#$€aP3 ”™ƒbL²,–0â¬-#$/}EA¦½RÉÁf#$!´#$/7ÊS RÀÀÄ/HpÑìtÞ8£”‰Ö6) XObûd‚†¡’%ÇŸq‘ãßNÂðM@DÂQï!D*ô‰UR&AWC;ìÙ% ƒ¥#)'£° Fî\I¬ã·…Ë(™†üxå\ÓϦ#)T?†ÂÊ#$(¿€|O§”MîW1 ª8tîØ`!ã$úÃȯXÈ[MIT ˆ)m)SÚ-C-5ó5Ú4Ö³JÊ{+¨ÚÄÍDQ$¨…üðtñç<ü»}]¢ŽPì·fûZ¥Š `mnŒŸOŒš§•³Õ”8èQùÇý~v ö£9·æÍ¥¨4¨aéíŒWšêàÖÛÓÎwgë•yL¼óÆ‹­ò‰­êÈo¾wÓ"tÚ¯y#`”PP]•Œ6ÀÞåA im•"p£è` rÁ“ˆQ&·‰*†ÀYˆâ=ë¿Ø žOgX ¨’D’f'DTGôFæ.'.íŒÛë¾Påó:‹ÐNfa!H@ÏzO ‚A;Q+BCl1š!7aJ(ÈB˜“üßo q“„“gGA7ï†õ3Ãu%\›9¦#õØàQ€à› ²'‡­JŽ–´ª*©ZQ¥Òé©NÖlÃBFk‚91o³mx6Â#%#)1ûÙ™&Å0Ù#%9$¹Š„›b JD3¾JR™Cî#$õiÇù²v?oq¤ôf†fâ»eŠ>Ž(q±é–9lQ³ýРøü¼mšãj'ü?&›|.ö%áršƒEζö™œµË3ÚQ•ŒªO¢²uuó§¤=Oµ4Íz+†¦*f“‚F¡Z¶0B(A‹Lkl ÃT””›4–ÙkMRlkFF3M~‰]&‰*#%K? m®j¦eSf6F3I´’¬Ú˜–-e[+6²”Õ“kÄZÅ¥lÚ™K5©¢J¥b­±´‘¤’HÅ"ÌÏÈóQù©„Oö?;IÇv²[ Ò3QÁS¦!Ãö¥€5$Kk©ÊÕFÔmQk©m«•»7ƒo0¨Áoêµ-z­uo¿P:‡™ÛÓ¿!#%ê ²b5©Q$E¨F2]KóêoŠÛõVoãÒ«íYü¥è$”WѶ‚.%PÆURت;¢T!cÉ sÝcŹ>% îÞò°0\#):÷m¼HV|Õ#$îÀSÍßýŽ}xÅd#$¢QÕî ôÜ'¥9gö±RýD„ zb–ˆ"T‰öÀ¹KÚ–é#$Šñ¡},ñ ÙÊñ-Œ£Ðð=ÙcýYí7mÖåì#$”W#)ÉÄFy³ÊêVšÛ< ,Ö2¶ÓKÉ“'°ý²¸ ƒ—ä[ 2à–Bn>8Ûðõ*žr'`ò0d‹â3¨h…pvª›Q3Oy°»½x==@dï î IY °‹ aP&­É<yIc_pqCƒˆJGÝø®˜rÏñ½ØO8ŠÖ0† «#% $ŽâPþ <ÃGNB¦ 0¤ Þñ?G¸ì,ù¢l,{Pûÿãûã–•ŸN³k#%4GUÔ/ѯ¶)údû{Aé¸Ëj6JÝÄ㌢u1%z¶Œ‚$ˆ`é‹cþžGF+D/âׇñ»Cñ^m<è„!âµÈÚe]ͶÉ3’_oÜh¡¶ êó¾-ApÐHã0´EM4ÛÈB û0‰¤ –|<#%1×jÔ>Nòý.’(d($EQ '‚VW-qŒ Ÿ³—­ò¹½ 9>oôÌhÔ#$~ŽáHå)R¥,¦üÔZúu¾|óÎçi‘qº#)¡,¢‘‹íöÐcÒ—ñ³ DP8#†¡¼ÜÕ†c€ÛexÀÆV#$0`"BaKSrÂbB&²Z°ø\73±íèÞo˜lip[}[h —6ÉiCj11 C§#)gï(ÒDL6јÂD‰jP1Ê€#%à žó!vBèïøçËú¿&×n{KÕ%‚å3‘«…ïaÖóxÐÚäÊ„&Ö#%G£Ã±sxFÊ®ÉØuhA × 6mÔÍ/–o&ˆQOñŒïÓ¤% î›(XÂ(xÐ4E,„BD¦„ƒ)8´p(EêDñ ¢z<¼)y\UG!Ôîñ2P1êæ‡Âr󇧶¨X²,ÐЪ`T¶oša-]Ôž´üS.ÐÊ ç²ÊhmÐ,‘¨%`÷tÑè1Ü?PÌÞà®øöÃ,È€ö’CÌ!í&C6ä#)ˆD@YhR# ÆSQA†ØFþ™ø>2_}m÷ÛQA_™ºU+ÍÛ[¨­úÞW…ñ¾heãÇI¼¹’HJ»¸¬éHˆÁÅTU-$äé¨_* Š¸‘Ãè*…"­7k *#%¦â7¥ÚUŒß2Í0dÍÖÕµ­¤âqrcš¦\RA„%G¢#tÃìż8اfÓ½]€ÊƃXcÙâw4ÎÆZgwöÝÄ5»¹U÷£è¤_vUcŽ††Ú 14®\¨#‘ l6õ¥„æo!©€Ÿ$‚Dœ”/ïHp¿>ãŠÝ!b=÷;’Ÿ†þÞrk#%Â_SýsÕžCl ÁãÀ '½²æàÈ[FR(ÚbÛ&®Jª0 uV- £ºÜLî®Ê¨DÄ•Êæxè\hDM¸åFx|3!ÍM¢pÑWDó×(£W—“9©×..NÏ8#%Ÿi é)bSõçnê0†ßO#)ÔêrÊtq/–÷†6äOˆ…§WóU¦®äLÙÌÓL|3’øàÅË ÏR¨œíÅòÖ³¨%®¿Ùý©ë·íë–r•:è ¶lo.7ª¡X$QnÁ¢† RZK-…²å£ Aæa€6U¸¥Ž”È)ì"¶"¦ŠÍþ‰Õõ"+ÈÞmÝCàAÝþM v`­@&>¬=šmÉàt`Øé`¡‰¿«Ò\8Á„N J:éܨC8§^Ì#)Ì($«U‰`ͽ­‚¨ï(ÙCGåË£—Ÿ@2Û´êÏ"l í·š!Ðû<ÎO2'£»XÞCO3Z®Ý{Ûz`K"[KÎÜDal… Ø¥q"!·b#$Lb@ hÄÈèÁ¸Ò R"*©T!`¡#) $>?73™ÐQAl Gûi½•ªAßÄA°&Ô›£jeUT@HO¬þ«lþßzÀ­ŒCÅhŸa+Zi,j±kP‰¡ös=å¯áÔ)·w¶õÔ¬cº.™p¸±ªÛ@‡ÚŒ¦y—Š2m¯.w6M}cÖ·¯€œÝ­d›q ŒN§+#%b#dd1ÖŽÐ\Ô-4ÄÑË1éBkTÅY¨¬!²«›dÓÈK¼¦y¶FÖ (¤$™8 pÖ!—jÐ#Ca†·ÃÐn,ªˆW!wD 0ðb¶[0 eö¯¢#$ÿ!ÁÂø™„„Œ#©T2ÛR4X7©Û¿Ê±• Yü¤péóU6u,<ÞaÁ‘Ý«éBöàM“þ÷cÖÈiŽ'D÷/Tôõ B0 $ˆHã#)b‘e¤­™hÙ-%[¥5_¡_¥·ëµ¤Êë¶ÙÝÌ[’Ô#)Q „CéëNÀÒƒäÜ1ù˜s…–B‹›¹¶©ãñ[€E‡—e¬Õ¨{fáÔqá¸ë2öc#$W"Ÿצ{Ê:š?†'R&A.ìèskÒœ Þê<³ˆUåŸôZï­o6Ú58U~÷è²- ²ó|'ÂÃRy-o£5¸Úa$Ù6UoØò™PÐþÛ×Òc†þó{¾2}H`¯”M@æ ´¥ê¢6`C°ðÌ:æƒ./‰dÏFg¤É„‰!2dÛcéÓs±‹g¢³|Sòf²f̓Xf­0ìnd«#%‚VÜÓ~·#$Å´S-3JjÑäJøKºÆ­žœùHÉ ø¼±ÜÛ‹n¿œÎÔp9ëà–«°ÉÒ.ÞGj¦4&zýrøŸÝ뇣ma±/ŸìŽaSYëÌíðõâyÄ1àŠ³ŒáqÙ´ôöýW›'c#)‡™¨Rß\ °4bbU™B˜æ+ÌJn.tþTØÌúQÉðLikâ†'XÏkW®HâuYaÔSZ·°òü­#%¥÷ÁYM]¹úŒa;ê¢IîI3“§ëÆüç_^ÎOr—»RýbPëð5d†jØŽ¬§›”BáEA4¶tCCsþÍsíØ4d³»\ÀçÑ}0OF#Ê[ã‹és—µ‰ ³©ÚÑF±‰Ø#%5"+URT‹ˆ¿qRP!Ø/žøîå´#$A~GtºOúÿ¤¿,+(ˆ ධº%ÄŒ›º²Ø\º¶®éGƒ]Yçnçnçwµ+mL­oU«¦ÛWîM¬•ë_©&Ä!41¥BûMë‘s¸ééätãàzˆó8¹(’L>oßþ| ;p€äš´¶t’¤4€#ìÚžü’ ñèÙË–MÓ7Íys í®=l ½Äìsn[ˆ9à"mQ$ sÂý¬+4æhë]["tÉ3‡Bƒ(u#)cE-âj<„–«b/Ôlweõ0` í¿fÄ!lAÔý¢uü¸š#,vú~BDV_Ïa‡`568÷Câö-QU ÉŠ”œŠ·kòÞ5uT6îµuËrì[«¬µš[M-hµ))fm­™j®«cEr™x¹&•Ö­öKB$# ŒQFDE‘A9ØÎl#)x ø?WÜÐÓ_DS®©–Ù‘ÃRØ ¿™ dÛ»à–NÐùTlmp0q¾¾ûVc…„Çð  Ìý< Ànj‚˜ârÚ¡–oÍ 5P¦o°øDFŸhˆ24Ž2$t3i0Æ°°à=9ý-YØÄŠY )AŒiò㾃§óî}vÁQÃc÷“nÒŒM¹®7ì#%Áu,ëÁî7)‹+X6h^¦oöWY·QïD §ë¯1°C/J1n‘âtzv-¼ÚÊøü,3<"#$iD—7#%cH:V•µ‹t¼õ+{ã âV#1Er•¾Õ#)OÓt“\˜Æõk¨ŒÀÇêMb“d`ÛúËæÂ!±…™¿lOù?Ëþú_î7ëP§ó!&R¤â¨Ö9³æ¹”‹âÅ‚F#)JÒDøvÒZ¤(M¢9ã¥][¹”4”…H­±0¼d#)CôšËÞÐètÉ£ôµ.Ǫj™HW’6Þƒ`#%H(3€Ìv-8x$Å;`RÁñ“¡Ã–×$PÂÜûÛ¡‹G‘"`÷½#%‡Nˆ¡’Ëô¾IÇ‹äGMé«c–äP5]Ô@}ÿ4ÿˆYäýÛ) $y”pùUÚþÇ®8èg*«ß&ᤎ§f¸#%äxñëW»ƒù_¢ÈÖñp™³ ÍÕ˜o“…ñ =Lo¦#$ö ¡UïÌ«<ž6i´ؽ¶#%Çðb0ˆÃ¼;àÎåéÒ(íÒi›€¡æ]&#×ÉI…ò#)>ê¬êIœëPù²– zr\Ckùƒënwç²Q—d§÷Ö #)n¬Æ:×i¿LPt5;ÂCPÂ)´ÏU†ŒÛ©™¦©²[Êcî­ŠÑç½JèXÊ8ýO»CbXFä™a¹÷¬#̽BΚ>}#°åéXj3û#!CrjxìíÔÖ08Ø+KV{ÜLèO53ÍŸ ô™á€†nEåòݾz¿£1HŸ€‚1 à„•3; döt! #$˧[¦qŠ¤’ÔT‰Æ,–X¶¨µmFÛli-E©–Ô›XÚÕã–ª6×-Vû)Œ.ÇSC»èÓ—EÊåQ¤¹–¤±P#)„?6rªöGq±$ƒÍÓ´àv6ßvþyab& ™<Ì|L‡_ÌÔ f*‰#%Už}¸}'6˜(eõQ·fØMj~‹ðóã>Šcâ×M] ÈoåµM¶H0I#%±¦7C·ZAŽŽa8w'åæSÆ@Ùâz2¿Ùûý` ?D@ª¦‡_>ýÎ#$ÍÔ(”“¡AÌÀ‡xRW€ßO,ÓÀº?vƒ»_¨LÆ‘î8ØD ÜÀæûÜæ'²ăõ3Ž]ûi¾¢1Ô=4=•¸¨ wÄ0ÌV1yɳ”dH{³wÅ‘KB}éªm»J-ÿ÷¶Ú`”&ùÔ‘X|‡Ù z‚r¹6gá#$û ‚“Õ¹#p>Û_&ãßÝW¯q»‘zÊ)#%)¨9D¼@è€<-ÓT€kã1°¾Ž· Ò¢ GGFÈDY?Ò!h(îNçA8Öý]-{¥ë.„6¢ [n#9†ðÄŒCdPÁ4RÕ#)€ç¼=þ2Üác¾W_n§ìµBV‹—HÝŠÐPÊ®îÛ®.ìÞëê ¦2ZßE¬ù3èŸN³ ã-O%j½þCèÙ£Õé6ù$|E¯¬í—¡öÓáù2d|•´±<gíýø\6x‘KßdÚÙ§ZpÈïÜÎ^1ç\K€ æ~»!±qÀ±aMU<ˆô©2˯môŸñÛNºíÔó}~Ñ>¿€^û¸#% ÀótM¼: (¯Qí ®Ï^ÂòßÆm¡ò×#)Uy¥¶Â‘U„Êæ¶Õb#H´0~C€#8e+ÒM&]LŽ’—DJ¨‘ÅP Š#%j¶¬ŠBŠIÁA¦’$&›R™›FÂÆ£uÝEz uKùžñà"Âë lÛ,بZ18hÀdh–Læ†Ê`”Ð1SKC›»#Nn$“0U5$2H¢cZh%`~jA¤067ï͈Y=?l¶%]à)‚I´j¶Mt«›ì•ÑfÉ«IW:IyÝ»¶TšXÙUÂìKë&Éœ°B1`âT(HªYE&j…™Î!l‘ˆ1Q”ÒË=I^6ò¥¦Ê™LP#%F¦ÖR±,3LZfÙ$™´Ø¦³2L5²–—½Ûۭɧw9·%E7]\«”í×|]x×›Îð½»\¤Tª‹zö÷³a“LÔ<¿Œ^qóÛä¼ÉqT UÌÞ˜(fÓK0 ˆÅZx9³YV€A nºG‰éêÌcuÛ¤Ò&Óµ6;•VŽ#%¾ÊŠñ„‹#Jíq[DŸ.<˜g‹o`ØP7ÜŒ4×b¦M¡»âe,ÚAí³°© ²EX¶¯mt‹·ÓºVjK_Et™ 4æÌË%ªÅ¶ó‹¤l”ÐŒJEÊÐP2 ÁŠÖnd‰F;\4d2RP3f´ŒB‘X¬4Ô*Q˜b’#Q±4 oqm,cJÙi-+BA)#)ês6ÁŒlI» ˜ÐÆf;jc ß».q Â5àb·!YY…¶ dâÒ«¡×#Ya ‰¥Z &LW=Ù¶²C$_S#Ó>ûL“aB4SØà£Ö‚&ì¡S<¹Ë†ßjjá4:éTWGˆ'ÊÚ™±˜ÃÓÒcz@­‹ºáT¬)™5;›f[:¢á¤´ñ1 -bÓ6ÞÞªƒ¢òÄ*ÀÀx3éÈ‘ò5Î#%F.ÂýÆê#%šnX¯h‘Ÿsù0|äøžKeÎÓb6LÌ4Ć…·§1$Ê æªWz%I]Ä#%¥¶äDdbŒå‹$1è!f•O#1E“#l3&m’XªZi½d[Öõ ¡G·— ´&µ¥Y¶iSYpŠÇ-Š”/’ÔѾÁÃÁÆѾ7möötÖpÀdz m0¡ºe×zU¸JEï#%3¡¥¦!ê$åJÜ,eeU»côt!;ãÞ!`…Á#)"¤ÀÂÒˆ‚ Hij !° Ð&•" Àa€LAŠ#%Œ ÑHQ,F#%’Š¥Š.ä˜%ÈI.%þ ˆÙ¿‡OQç¨êl#$kÓóã=—j]›§êx3³qõ÷5‚Õ‡û±bÂe4~©.0¦*àû>Lh™5ë[ƒÖ¾·J¨MŸánšÒ[~Ÿ×¹âí…μZóÎÍ"ooOLzÅsÝ4<5¢àݶèÓo=ûº™)doowIÙœ„+=uG«8‹¬ºÖjcfgME,«8ÕÅ™)ÃĽþ‰¥dõë]ëöÎïc𠌼@„9Qy·'Q³yÑ]sÇ™· 0ΟDð›ìldêïß3\p6´°+ C“)ßpxÜ2©´°›"Hº®mÊ·{óO†í¹ûûŒA¿=EÑA¢vÚÁ$yy«@Í4bK~²‹è}xb|¡ÇYR:{ŸŸVvuè›÷"åÃÀ{³P’X§„ç#$ïŒ<ৢf›²¢©–€[l¹$‚Bļ.ˆ«µ² j€6… 9Ø&n}”Rô#)‘±³˜ï±m_u8eh&—®8‡ú’@yÂ"yþ™‡—8GÎ[Å$$,m„ª[}uü;Q¶Ö|Lºo,ÒÜÍ>xÈ š6D¾•VÈš6%ËÁ¾Z£’HE#i#%Õ]ZñÒÆñ­Ù«p¶Ü¶ÉUÞ:6*Ư:åÓWVÜ«»s!—/QN¦’Ťh¤ceM¹Â§vÚ5IYTÞ¶Ûů6´›¦µÛuçž1hÆÖÕ‰6€À;#†DV-™WÆÅF7^™c"“>NgÇ ¬‹i$V#%#$™D"…E… ÓX%¬ O<‘Bê\#)B.øÙa˜”¬—Isr´×Æד4Konµ^Ô …`CÞòß&Y¬VR5$D2íQ-oº™ªIüÇUÙElVA2”±¨Ö4TÈÔZ*-¢±Ze`±ˆ¦Tl›e$ÌeS`©™,cUM^íÐHq©¤#%Ç·§Cቊ|zþ#%BHÍÝ÷µV½øƒIm¡4d¨eS¼OŽÇjÜ™#ßÌàáýÛ”0¨ê Ä¡ÃeLi´qzûN0PDŒ€š:ÒS-Eù—<”Ùbªý[òµ­Â­xÚ麨¦yºèÕ;»tµÝ¤¥¶¹m£&±[º»JV´Ö²½íÙ¬©Ab€ ÉRŠŸÜ§`qÅOÐ1:#$~{)_²#%!¬C˜š°&1TP#Å!¡UÛ€#i$€H($s£{jRÜutp h”ƒ…Ò»°§0Cl¡‘pé꧓Eã!ªÚ8yÃÔ\J~Òo=Ý•ö<@öà£u0}2/ÞùQXA Ä» ªÑ„È‹éšnBæ;Ä|7pDÿ®¸™·7²™˜Ìn~[)$Æ2»™&š„Á4‘3ifÔÓ¤‚M0Ì–çjmöv™MÊ[]ãí+™ú÷8òï9„›YxLh&!Ð&;dé¶*ßÑ=˜ãTî°…#%l‡2}¥t—#%˜U{FK%j¼¿W®¸9ÂérAs߃f›‡É„Ê´²ŽxÎMË°²Ø®‰h¨8¢iÜhp³9¢¡†˜—w³ÜðtX*þòvЬDBt`,:ÎÌvîVCêì7^W±‘Sj›'-Ÿ³OËƇ÷V=0TLF>=f’åLáZUI ½dï|È|Âê Ì2Tãø@FAQP„W@‡DB "°Pÿ@Ïë4ÅI ¢–:º­äžº‹dÚÖ5hÛrÖ×€äqóŠ ûBXýáÔ9ÇÃÃ’2ý0$#$b@ ÇeÌ}Å!ñgGîLÞ` P…JÉm5×gÇ-´¦ÛtÛ^Äñ(#r4Dh‰xa"àÚ´4}ïf¶Ðˆ6IK#$J`•o¼M¶Öd˜ÅU^,ÛÕî*¼î®Äø]ãk©˘µŠ¨¼¥ï#)±²¬bŒ¬HáY¼Â±æ“µ²3(€¦`Ûe£lÕ©J˜ãih"«#%aF3ÆÐ40¡Pˆ‹*-lX#%/!•°“0ŠŸº%f!L"\˜€¼h>AüwŒ[i±8ÓH‚#)"0õ’Êy?—_)P¼æð”…âÏWD?â}’O«ÖÐa`ªsë> Þ!*ÆYõ,]‘³nÝM³ÄÁ7cÌ5%¨Û·?fü·fÐuôeU‘; 1’BB#$ݨ Š¦(@Còõ½ùcóûÉs C變†ßø‡|4ÃûàÀú!’`ücê—7côÿ@g”Ïð04ÚŠ¤¡X÷;À,Cvúï¹÷ËãN`ÇÝ#J&}¿–ËWYÏ›‡ñ:hò7züŒë}|vvØÿCV¢µUÍ 'Û}ìèúQ40¹Û¾q½Ñ:Ý$>“L‹pP#%í€Ä«#%4ØϘâ›Û#$HgÞ„ÐqÅë²6Öó;Ï\m6¢¬Ö¨©ji-8  ¶.‘JPô)`¶ 2H¢´0Z¥!²âØ ñû•?hA]"®EykÊvNøuû:‚KKä’_Ïš]¾'á¬Ù›³–C¡Ó:é|UàZàsM¯mUªËšÍƒz|Ý#$×àê?¹ÒDª©EAJ‚%R0h ÒÒ†aD,ÿ]Îê#)¨•#$1{S ¸ÿ‚Ï…Çx!²7*B-P%²HÀZˆB„_˜\°'Cò㊠$Æ‘ª—‹»Îë#.Up¼]G—w‹ÈlJrÜËÍÆÓe“I5 ¥5G·jm’±Rl`ñº.mͨÎÓy¼»q×]³è\®îÝ"¹x·%QÊio'“]§5™c@íæÛymDVÑY*±¶‹)ciå¹K3IRVy×t—NmÝ™ÓrE*w]ŽUÖ\ë66Ûb×j[sÔ¿ÂêÖßvò—U_Rz`#%† °/^ßFð|öC°·a 6r£öy¨€;”þ®ä-F C¾Š ”'k,ª/¬ÄDN)ӊꜧ´; ØCÐR#%ÄøÈ #)ñÉôǯÂÔW¢û#%žÛ_ fÂöyòîŸe—ÝmÉøöͧQìÕ涷ܖÚ5™€¶¨Êfhg"Ân|ZVYA`bÿ`ŸÑF0PÎtûxØL B$ˆ°ˆbªÐ¥¡–|µ|Í~ÍûMïÙv¯ÙªÒ×Õ˶4–›Të®î¥•‹›º×³[¤¯Ö÷y‰š‡ð «¹ b8hG§àA 9¶Fn”ó¶ßI1ïtÉ’J#%$ÇbQILL-˜áƒ(‹ñzÖÛɶºínÚlûySâ]umÚ·d¤l0Ð\4:g³~õÌL’„ø*%gLŸÝÀõãbPÃÅ`”`{Ûû&‡¦°#%¨ó#%ܪ*ïˆ,€DR 6mYMªU´«,´ß~‹À vìù Üé €¦¹ˆè«„ jFËI3TkmCmQWG‰àÀ7¡¨GqñÓ$ Œ0•kûòÚŒ­#%jš—·µñ÷@€ž9ÍmL÷W¬]ˆz"$§(©­ÊÍ_êøD‡pzF¡&ÐÐÚz}¼ïª;“qÓèî÷mÝùî‰ñ´‘ˆrB!§àKÞþÙùmÊðr^(ÁXq„±ž„#%0pl‰8z-‚¿—š.µ#%äE`*͘Ȥé{â÷ båpþ€@cÈõôE·SØ(aþÛM>]£@R’`€2H–:É-CJ‰Ç„"@äTW‡n›Xø{íz¼«Igö¡ZDh Æ0#hM£{€¨ìZ„%D!#$ „Xó›e46<Çåw•åoµ‹Å«š/r¤¥&HÀ!hKAÀT‘¿'VÅUwˆ"¦gF £à­'¸ÕâÇvvUX7ÞÖˆ²L ©"ã#R\¶7‚6ªª3ŸÐK&YdɜЬ…Ô‹¶)JU†±QŠ©D#$¥I YT°©TT nòâ™DM#%˜¦X‚r#$5)7nÜ›á4qû¬#øŽûµ#$¤ó͉ãXôC{ñù‹Âð£IdˆUP Œ¯ÍPP¬& ”j$!¸ À]/c?*v0ÜöC&½´œI"¡Ä!ÚNä'4”RHÕDU"J!PªŸº)ÐDü" œ¨+ê‹2W^ÚvÊ×-Wš#%‘¨T„VX‘Š-!3#$gŒŸ¡ù¢Î¬>g„Á!—æSþDM¯ù}Òöl¹–[Zm,M.ú¥_c[_NßWœ¿Lóºõ¯uKº Ú¦¸ºÈ6ÛrK&8(BIjº5GLÆ슕ÒÙÅÿCPÛ4´(«rx;¿è•VN#%œ5#)²òªÛmW»LW÷N…èxPé¦x#Ô£â!¬"xZ·†Ó3´<„sÀ¨'éÌO¦;¸‡Œ¨¶~j Î ›à¢n‘ü`¬Š#$ ꡦ +H¢í«P@æ¨õ„#)'TÉgç®›[÷À-ö(u]¼S[Û¼‡OëáûAÔðÐüF ¤,2?ŸÖ¿q­dû·—ç(™(!£ðS@·é‰ ßóô•ê+íõÕëƒ?"· PhžúØßòÁfÍPQ—áÆ ÷¢‰#)2&Óóc<Í$äÙ%á¥Ñ}GÛ ÛAä¯ó¨^Nã[{ð…—£ÂùTÊK*êÎc8sæU¹ÍÑŒ´Òk¥ç‹#%‚dgB©3(zk±•”’ú™u(2‚¸©R +†AdÃ6ŒH–Ÿ/“½;Á]ÆýÍŠ%ùAû>Á·yÃÔ@ú‚j™fûž ÓÛèkÎþ±§•÷ÉPÑŸ[ú^ñ^#Íg¬_–=×Å'6s UF¤“ÃðìŽUTæÇ2KÙóNBGí}Uþ뱆aóf8” ß>s%µœW½–üûKCz4È Á"Š ­Ì]³—ëÕË—‰$¹Õøê”$±>ÌÅ£#)µ›ÄciiŒÄÑU5£ìg‚O#%\†ŸÝ0¹~/dëüsŽÐ4J:q‡YÛXòˆË`L§«wF}¤ësKáüo"‹`±o4¾gb·¦ xq=ë@}®ÍH%cæÉÛfõß|ëA.H4mLû¹Þ<»BêCôÕRn°Ê¾güUWÇÁ™JZAZ\¯[ÞšâOŠÞ­÷2 ƒIPöÖþѤA°^~Ūuæ¡éå¼ÿ}”ÖIy#¼Z;k“‚ÊáÄŒ ¤ºØ±ìlXˆKz`„P£h˜÷ôÐ#%(*HU°=s²«.Oa4Ýî/i0UhP·`‹A°Ìª ä÷õtôíÏ %O‰g¢]P¹g ·³Ö8 î8‡aDéCdC§:JÆ%z‰$F+å¼ó7~Ô˜CQ°;güÞš–.Š!hq,ÀhOiçO¨ˆÀ¨H¨”˜A´ÄTÍvœÊ:wuìA@Ú2=oYo.¢!BPÿÈ£åT¬ü‹ ›kR ík„àjHRI5_Íò»#%@Ýû²×6ý·ŒzÖ®[^M’ÛF‚9ˆƒKÛ$ŒâÙš»z5JX×ÁÉýE½¹B‰ jjšhêB"ü±åªµ$ìñÉî‹ÅS½ËÅ…=¥<5éÕ–V¼XÎPÚ<L€É;ÕKÙƾ%‹ž½/ŽÇ"e=¡eåe¯bjIžaˆU±´Q¼T¢ÈÇ© àΚ“Â)ùëõæu†òÈù»–™ëš¬—ƒ„gÄ›¦–î‡Ð>ø'•NçÊOw*4Íh¹j2T'+óÛ¸tN#)¨53lw®±($^ ̲™w÷ìmƒ]ðÓ MO&ƒÆ+¤!è|vU]ÏN~Þ¸Þ ×¯,AÈ| ¢iÂí ¥›\P„Ä»(#$O¶€˜ Î'gŸøý¿“0¬']O®PÚ¾ v H$‚–Š²HŠu©Ø÷£ÐöÃàü¢°nö‘QBA DŠB‡nÒõp]€>û¤z«æ>ÚódÄgQº’ÁCoW Ç 5=§Ñ‡Ö{S²\ùyÍÌ4 p ‰‰¸Q#Øsâ.;~;C§¬úÌÂǺo0íÔáâá_•B¼¾v’R¦9Ý«jÛçÐxim¦×ädÒ"¼Š:È9CÝAðIÝ"xBEËOÕ^î®=Œ&]œ”@õtQæÚrQpQÊÈ(ã…ís1QßxÐpÁcä žö%#$¤"$:”;~(#)H"„"vÄX¤b9ñ¨g[¼-±€žÍÎ:‰5B£«ýN#%!;RЬLcJB@s…Æ-kìãûß×ñfVXâ÷CùJcEíR6ö²µfv©#$Ää>Ý»<EJªäH#$Ðxƒ¾4Þ{ip'›¦Ü0y~“q†¦ËªÑÂ#%·ïmþkóSA¬ÌûÝ3:1CG3æCq™¢#%}QAÒ\ç#"éLfeæ*Éh÷E¤¸_g8[é]•¾dfD­#$zƒ!væoÈøËpij{Qˆ©ÅÌ0@ßÏž0zá½öÒQÑF­¨˜Òo. kÖÈ;±fTLI9–‰5DKœ°_«Xàˆ0‰2B|mþ?£mmCAË4{s 2? GÝùòð¿³Ê¢¨ª'×(³jlÂU9©#™ûrë˜îO~ýúoXʪqÁ‚Ã~ý`²=3¶[^èBöG/#)ÅÄùè¾wc´°ªªÖà ¾Ùr/Ý#%ê’;°¦/”` 0Öˆº{0:Q BC˜Y@q líO×D#~½k!ԶX¤ó¥&™Rgn9Æ°H¢I§NNêåk¹û÷j¤£XÆÚËe4¶lر-jQª›?&í–CsÊ”/¹…Åbaۗèù KU >¤ƒ*”ŠAd¿ðã1~ÿiw…&É™²DTÀi¢‚RÑ(–’$Å‚#%)°É±4) 6l›(HÌ-o¿}z­$#)%ä‚S”Ä¢r”·8ÁùVyppšR’u1 …NÆa³”ØpˆFÓˆÎ5‹›Ç< ²aÛKå,Ú§¾¬ë·Ø|å÷MåDf¹ÓJµ/¨N» CaÄL„ñ^ªˆ¦UY¨¥ÍK%ÚÖGî÷eï£i¦A?VîÒ€HºëÝÎó/}M³á“bãƒ^°ÂíØw³yíPèÈÀñÜ9«PÑÓZ+/JÄ#%ø˜'BÒNŽEZ¸Ð~sòêÒó*WÈN!'Ýüa!ø¢±1rTlÁ!­¨ñªÚþi&ÁõÚ¹Õ\pJƒRi¹#$Iüˆˆ0µ–ÁTÇ)O3›rêåom/%…¼&¦Á2Ìë{HFj „dÀ#Á¨Ð(1‘¨@£»)(Ûi%#%•ƒÈ*%+lË ›hÆ@Ð#$±€Ø,aX‘<4A#$ÌPÁ¤B¢TƒaSi,jþ¹+µF¯:íi•r®\È219%œ5ôæ€|›]Ù•tRè5J å¥˜™—HG #ƒžÌY˜6XÞE|š‰FŒä­c*ÿF½Þó].C<dâÆ. fšÀé<e@Ê*9“sGIö±a½Jw©£4¬*ÄqCE#q¸tSŠhid²a ÚÍMM …Ýh¨B‹!hIˆ#fG!fjƒ)EÕ,Eë•%#IƒpŠ°^ÆÞI £§®ÕlFah ƒL!ÙB´.¢œÑT†M¸äMD,e‘¨›PÓ‰§Œx 5ªØØÖ&á$Pi«Ç¡¹0„eQ2¹UiÒÇr&ñ`Ç#kDÛW Ö#)¹TNÀ #)ÉhÆ:Ú¬l¥[ o0™† 5!§Fâ‰AŠƒ¤;Yp+ÙÙ¹nñïc™ww´M]]fUtiÜh1Ðf#%M2ïmkK5£>cÆ1š7µvBܨæ®· ÝbË-­QÆV69YZ±¨Ô¼\}µ˜Ã’ ¿?ƒ\qÈÐcQƒí#*±.FA cklÛ È'{B°ddCÔ#%膚Ö#%‘׊Å#Ú‹SÊàÃRÍÓÙßZ³¸£…!ÆÜÁið£p¶AŠâä£#¸TqV0@;‡HÈÌå81`KV“Q¡‘ T[¡sD 2-™A¨…š‘¢˜s‰£«„35I@0Mqåø„ çd×BÍLFZx… ªÐ"Àl¡IH¤5’´‡"£Uëˆ CKÃC|ü|y¢Úzªæ¹S5eÝ¥ëRÙ#)ˆ#%T”% …(è–$œª&±§÷LA½Ýò1¼3"¯”€Î^†Ûxà莆 ±TÐA0T‚"m#%,-srÍG‰(›Ñìaœd›#%=®Ñ&ŒÕ"ìDÜņn,f½´Ìí7LØ0€Ñ¡#%’£X´4Ò¦®6żŒ±¸Öé; G¹=d{`jòTJùžÃ#%¬å.ù¾ÇXÞâXP®®b"UM}ú4ŽÝ#$cfѨŠ© ç(†ÑéƒT¶¨íñ]ŽÚäÖ1@‹uË4¨Ž%EÛäì€1æé­Ä`Ñ°EPH€‰Lèš" ‘¤´Š(ƒä€Ú@¡Ch*±%EQ‘R`ÀF#ˆ—´–ñIÄÁi£ëM@*Iûz{’5JS”6Ò¡Lþ4Åb¼×J¢M»Þ®¿Ùºoݧvܹß]öWêZJ#%µÛ-EŠF,HDJ€Õ+ª}ÃëíÊ€q^‚Ü ž—uÍœÀ**5AÓP Ýœ¸$Kx ºŸOïËÞôTAöaJa¥ÊŠ5ù¤ªÈ¸&áÝ$D!#$I‘ˆÅJ3#$Á.[\ñpù«5#)@ì=UîBsuÓëgi(&B‚)÷NJ’ŽUŽV¨Á PbüD^õ»Y!ô´®HÚN8±lmºººU·ÅqÒ‡wFÁ„í•Eªq¨bP¡›™ƒ`‚{מJ#W×Ý»}ln6§5¯ß+<H^´a¹®ì· Œ6oÊ#)h`¶gÚdÇUúþ-A¡[d=&m¨1‰vw’ÆÝoÆ¡âuÒ޳ݻ‹'t„YÙI€Û㺹›AIY£#Aù„‘ ý-vbŦ/’µ¹NÜð¾q€àÞÛur0O$Pš‡R—ΊŸ®èÌ‘&/7á—“„¹k”DÊ"tÀ»‹™%eí;w‰¢a9}ý;^ÌǶֲxÝ Ü…¯z(#%m6ÍôלåSáÕ¯X¬ø%Ð2׫ò#ôXv¢#%ô~sèub«frIÝÞ/}¬Ê6pŸÚÅ“ù }uó[~ “BGŠhW¢{Žòa#%&xQà`¡Äˆ‰!k»æN‘1rÍö7´ïp;-P¹ÆQ8ÂuµìÑ`)Òib¦½¥T*›˜;kMM ·Xè(#$ý?1`{ð•NÌ‹k#!:…²!Ús;Íbb"ÊQ`‘@U>æCèÉ0öÒ•J3ÛU3·©.\2'è~­/•3Jš·-*  ‰ÄC\ËJš4áÇA(a¢¦#$2¦ø5&º29@q5ÆtÒmú`E€„-“QUýôÀÓ+åMÈA˜ÒPcr#)66i=\£W®m¼Zé­õ«v&¹<ê«Æѹ«•76ºó­¬Qä«!¬j5¼›xÕãY5ãm£gwËr4k&ÛÕp$-0Áª˜@¦†Kp…æƒs”lZÖ#%dWÊ#){‚ên3( ¼=ž ó¡{#F4¡e$B#)‹œ ‡z‚ÕéSûpHv’0wömÝUÇîÈ<#%(8;å(üÑz=JQø(åö¢°ˆ  éþJIcà§J†è €UDÙùûÏ·šäµ»4µÜmºîávm:ª¡;{‹…wÒiuH#)w Ø M©",‚(Š-*¡H!–°¹`$PÈLKõ¿#%Ë}æ›m­ø¦Õ,Å’²B¨?»Àù [1Ð9Òü  ç#$M~ø‡›'Mº#)0µB°0È—Šb 碆&7•*y^Ö”\9Á%/#$S1{1f?iä ‡o‘±+­  èįH=§PÉ‹ÛG‚@'í1‹ "†ØÅ¥’2Ñ“F&•¤ÆÑbÊRRLÉh°Í«EµV5¶6Õ6›fU5™e«Ť¬Ø4ë}%£’ 0¬3 VÈ‚191‚•È‘>Ì)pÛj<¡”„dlŒ#ƒŽ""w ĨÖ`³yD€"­"pȱŽ˜W#)‰bM¡²¶2"ÄI2J‡P.T±A± ”ÌC2%0ˆ) #)Š€‚«Zê0B4_P‘j "@'Íù²µÅÚ[Â¥âÍÛvj]Ó³wuõ?UÅ$LAX¢HhcB¦ ´:ÀáuPÛ\EíÅMÌöâýcŸd‘;*}žÿO¦}U¥Ï-R¨¥{#$ùB#$¢A â•·íÉ”ÈMÅìƒäp‡x#)aLv|,:üo¦& LGŽ‰®ã­ÆEˆ¡z^•ÅX Á€Ð Ùú'|ÃQ¶WÂb>,£D#%<ô¤4 i5„[¦|¡ÖP= é‚ qÞ<òë¥uyeÙSfe ÷{j¹¨Ö±XÕj]Õ¯v¼Ö"^. c˜våK¤úáýDlK±î/FS¦\¼Û‰#$$ÐçÑ~ìaöº&Ҥݥô›é#$ö·ãhþù'ç¹hQn5ËôÚ¦Má+ésƒî銜Äâž› º©ñïåëçgC(Òe"¾Šâ ö賚Ӻo#)ø½J['í¼“Õu$qqÇKž|­Ð ƲíÔžŸ£´Á/Kxâ‰Äí<cn«¹ztDNÝp·OÖ̼’—#%M‡ós¯tå±|­Â8C—n¦åß™_ù9ƒ²;Qv@&ðÍ´­cw%ôxì:´×Ãáåž›¡êÊ]–Ý™å¤ôŠ@:X·2ŽûÛ~„e†j9ôÛ½[ئãßjÌ©ªfékG»óø`0œ8”èïª3ÛXõ̪Ԯa’ºðþJºá† ütÆBöZL4’8œnŽÛ"{ìöÈ»ï¿Gø{æû9âwlÒ6Dá㊠†d"xŸ'¶šém½]Ôw„æ¦{n÷%®†¡ 1ååRn#)Ÿ¦B_Šsƒ#%¾î┪ÙçÏèÌm-ü¦Z ¡¡‰ú{ø¾¿EŒt^,Q”HX¡TóZŠ•#%äAÖ(:Å’û©Nˆ‰ZîñŸèŸI¤¹Gׇ%w<´É?}‰ÒÅÂâa@µZ–¤{®¿Ô;ai­Ã´zIzö±Ì¨ë¥^_¬Œ¤¼3¸Ú"šZb¨ŠÝRÜiU³ö¹_ê}9nwÐi÷Úýþ= lªqÑh˜êCâz©9ªÈK8ñ i¶Ù{,UbFá3ËÚ¿#)1˜v-¯´pT¯!Î~×™ç8[¤"Ó=’ï~tÀá#%qQ½s%1Г¬>ÖpÁ¬Ú‡àÐÊa¨ÙvßÖ#%åÍg_* ø~b;M­®™œ 虘¶â­÷kÃÓ[é“áã§f´ÈË€çVè‡è·EA"dôð’I'wEœæy­Êª3o:,OíE‡@‹´o¦X—cÄrmLÅ1:c-ÛmÍé8â^Vm˜Ö)Þ¦Ä0`VŽ#Œ–áå5³…iÇüÄmŽÞ$rïbsLÚ§ÎñCh¼b9´§ŽÇ;o­n‡Ûû×]ÎОeä’yxÌÔoQ&ÚxS]±QãX‰éuðì8¤9dÍB8‚µ³Uîíä+4ñ{꧚öª¼æ{Æ‹ü¨äÑî0‰Çµn[›.ÜKc~H¿_'ÂÅ-{¼°^9ÌŒpäuzó|‡;þi{+*S¹ç/ÍÚG~Iñy§Íóóš³{É=/Þ’°p^WÛÅ—•zàœvx9ÜÆûW:ñ×c“Ó,ûfkOD»¿j<Þ‘(Ú#J#m»R­³žS‹ )u±lu;t#%_8Ùü*aª‡}¿­˜ÝJ<z&ƒ/„Áí7Ò¾÷”­?—WPÄïcé7^“ïéÛÓÌÉ߸uíÐ1¡4dEèÏ7Ç”qÙcPƒ¶å¬mÖœƒVp¡Ónåkw1O“ÒÞBm‚ÔRôœåÎ¥ ˜ß¡Mè,¦îlänÃÏy5SC#ŠPÐÞ´ø ×mÓ$a[̲Ržkô1½Ì-2ÆÕ'À‘‚9'Ñ—œ>Z‚š@#%´TŽ¨_1Åéª* #)J®œKÂÄÜfŽ™ìuLòW^!ŸÍw"㧖 H7…é è·òÄ™*†„&HmÆZëªê$æ”)¡ap×Q6“3‰•‡…Ú ÊËeØä\0È.#)tb¦qÉ##)Q¿¯×¨‰…Ô bÇ-H2€ 8á¹:>“öáëÄ-–4K³hò’Yr ™ŽA ­ù¦`ÇŠØ/lÄ­„†Ý$ Ü5R(ȺN¦ÁcxKÕANHD¸XD„Q0@H-†øhŽFMºxW^Þ¼qË;%·3™BԳá #)Î(çÀÃ-ÒIÏ!®£A25ÁÃ3Š€Æ‡ÕN;_/)¶#$9ÀùÄqA%XlÄÁÉá#%xQGÛUk挂k”P¨cX‰Hú&š(‡Q7è¯#)ˆ]4% I*PQõ n˜ªdæHĹ?!Q.×ïˆBí®0¶#)lq™S¾£Répû¡ÛÄúÍF6ž'ª!mÕú#*=Îm…Ì¢DGh´®ž!-É–ö’Åßnúª0â[?E´÷ƒà»£uk]ºcb˜ƒ#%±7¿`'¦Â0 AãjÙú›üôu[ÚW±9LÎGÑʾ:ÈJ‡¬!˜€²ˆ÷;­¶}ë 8ãÆòÑpŒŽe¸©ªÞBuNõÒ²TB<FPúèpKRÆmàJ01D‘šµ,yñ)l#)ËOãb¢q]ðø3<ïı(®áÙô¯VðÑ›Ûï.fÜÂN™¢¸­b0ÊV3õ¹iN® ÀùŒ‘ÔçMɾÝ6+³n9aéÇõ¾b¸ß¢ß¾%#%Ù²Lºã–鄺£6Í¥“X.ŽCkâ^Õí¾Aþ¼Þ=ãòç•ön¥˜-ß½¹å/ºí}ãW0ÁækŠò—KxÊ–¢aF<wŒÉ“²[y(ê§ÍÃsq yHï·Œ¶Ÿ=ÁÂx¦o¸fù‰/CwÆÎ3Ê™[V¨W<̦É{x09b%‰K/kKV%¦CîÆûi=TN*PUÒ)<Ñ,ûqÁS.•Ì¢É)/…@eÜ á8*jrbñ&°Tù‘æc!¢õ—tyÑ“Áß¹“ÍÎOÀí™-üY п6 Q‡l\a0…ëN6Ëð‰¨jœö£°NYqpÕ¢ð.XHSE‹8-²8‹ \½³Öëdyfgœ Ê9;ZØXm@ljae‚1™áCÞÕ²©«¨T45à£È¶Ÿ^3˜{©V“PØò•Ü5M(P˜3iA¡°€T‚;Ð8Ik#´âÆMûäÖï0ât#%GT9‚Hð˜mU’&÷ýÀ×]õûC–6¨a²l#%~ŸŸ(û}Ä-ðjÖ±úìÔ¼Þ‹ºÜ1ÓáÙ1ð;l›åøƒÊŽY’ ¿Á¯1.ªx<KÔntùw\—Ë<?‘‡œ<¿Ãû—W‡4;ýaë^ÂØÈö¥£HQž^’øCxCìC`ð8»ÍÁÇæ|ëkŠ)R,ó¹li݈ÕûY¿h¯m«»ÄÁY¼æ ìЭ#™¥KF™@¯#Á¥|kÅ×&¹8u]Û;­Þõy½¼m½m°lkåÉw[–‰/(¡–bH…›ÀP´¢¯|P*CRÛ[Ũ¶åª6Úæµ"#%±³¶ ÓEI96Œa0 ¨2*"#)¢Œ…JrAîñ¾rñ¯ ã¿b¾Û#)(PC€èôgwcTvºÕýÎ3Ì,˜Í÷o¤D¿bq^¯xÛ‘pƒ)T<¢$‚‰  U#$2 2˜JŽhnßsT˽>Z¹Vð}ðÍ}·XñËe£ ¥¿Ëâ*Æ1ƒF(©QL¡ÎùáF–†àA¦í¥ VS‡þ?Ó0lál‘^#)1¦™@ûæ•\“v#/SN0Á*Ì<òº¶Ô#)RÎ|óM±A¤+ ÊB4È¢Äpùq§©àË#%V®ÈˆÄìÊò”Ä5·€òJã0‚j4›M¥Z’)a.UXؘÆ4C)[ÌPåf²Fl²‹XT±‡tÊ]$…Y/O¬¶áí6 ŽBîMÕˆ`&j4àÄYeݸÅ5en°ÔMÀ(Çc^Ç{8cZhëŽv²ÍîFjh£MÔU¬zkZÖæSÇ‘ÂåËÉÆ.3VÊß ˜ƒDE¨ÝŠiU €å ªÅO.QÖ\16Ô³‚"¶&s-~›ÞµÁoDŒbE6ÈvÎS¼ÊΘ´Ø&0‚9Ü6(JR#M#˜t³LÍa¦uŒˆQ¬aNÖ•§¢C›#%,ªoW2¾¥dÉJÌ$»ÖÇŒŽ8Lº”UÁƒcrz¤¼}âµÍ#)D‚nKÝÛWm¶ñwfÐ#PŒ{Se"r<”+C׉ÃVñCDÒ41œ¡’±ƒjÚ7 ¥PT¡V·š#%&#%¦#%ŒM0¾5hÛÖâÛÉ’Ë+·®v,ÚØM˧Ƃ „Øh2‹…³wdeànkh0ÕŠócºlÐB²d)3É“.ßë9/ ZËß`xïh\(4Œ)±å°~Fœ‘ùÉ{*7ÚW¾î‘05½m±³¼Ã¦cØRDØÛÞX5X20q·ºn …°¥#)Óa]`wF:ьд´yÆpa;ë+`æ¥5J©XùyN{b¢ã™Ã&FÔ¡áYUVÒ5‚‚§ûl(ñj>31öeIÑðV"Æ…¤Zñ¢×ÙM#)/ i Çn  çaÌs)Àñ¤™ÍmÖ‡Ã[zX[2”ÀÓ&¤0ËÇ’m£EÀ}ØV®n`L3ÉÜ$g>»YŒŽ§â4ñ¥¶Å³<e`æecUyLjTËlÉbFÐÐĈÅ=Ó9HÒ TŽ,„ Î ß}f&º³ Þ °g~ 8¾d΋Yã/z»4vø¯[ØãîõËŸ)TÔF‚‘)GaeÝÆ M¸ñ1Ÿ0áîÿ1™^h0XB4O!}+J‡¡¤¨!$/‘¤(w"õNùP€}Eàœ‹Ñ¶Ïd£^É^Œ2(ÓUÚA܉Q‘ÁWžßB…ö°¯‘¦Ü'éqº²±eÓ®38™~§WÔð~ð’@ßõz5ô @N-²I›ü|ý?FÃëgJÈ ì~¯/^zx>zH10öœœÙ¸›ËŒp~¯/ÜKütuŠ#%–ªW—)™óñ›ÜõÛá"4ÝÒww&#¥ÇvèÈʪsÈ3<Ø‹g“ÝÆuêNÐ÷">ð:ž1pa8¡I¢«7¶Šjä~¬B¤ïéêMMÏ8;ú>Êš–Š€'qU_~¦¥z71µ²©”ÕhXªˆ’…´EK-H/@ïýSðQæD6ιíî«Îi$ BB6©QSIm¬(,†#%ASI#%TÊÙ¦McY5«J6Ä›IŒ1›*,i¦Í4¨Y%¥’¢(bšƒQ)Fš)š6M†•L- k 6aB‘@d$EOÚ_¨ó•å0TNX³‰Õ´LEõÄ·oDìg-ÇÛ7  ›.f'·p ò.†¿« ¿ÀC#$M7‰Óèí¶ÎFïMŠIð—VÐO0Ì(,‡…èY$C*‰î –ÊAça@mI¤wö›ð†Œ]û8ç#%0ÒMM«sàã:0þ¬xÂÆI#$Q’#µöm‰o7•ÐsÛԥȨ˜’Ä.!ºòI!uFšÞþcÝçñÌ}Úó7‡>ÈÀ½U âY®¢FøèÕŠZìs S¨`MçôÕ~µ~£m£%lšÄE¡”F©5¢Àµk›H¶Rj1X3ñÝ«™¶Ê¥å@)T¥,…”sà Û†•ˆHÂ¥ !GßìÌ.#%eYl}Ö^ :{ À Ï›h…Dv‹Xiâ0Üü™tj±„>—gCµEh¦¤#”˜ˆ¡¶ðrA¤E ºr’³[0 C,[ ±w$Ã!l$X¾C2ØÁTF£CçÎ×™F2”íïšù•îM…¦`1¬iG~¸³5 £31„JÀïy”¨XÄÄBŽ ™‚#훞4qÓM'øÐj%3äá ÉŠC’ ¹rÌiA$yàˆ`Ìëæ!r6ÞÞ¶» ™‰\µÄ›ÞÚú_¾'÷Ó•`=Ÿ®lG:óxÛÍÈÅ*‘ÑÆE‡}&™ XCµÌ¢L“¸ @ç0H ¡#%ÌÕRÏ[.¨T”KšB€Ä™ @ГBý¡‚ª³ † êßѺL#$Ãgšb¥*‘$@ èël'“öái¢¶oÙë›Ïwo>œgêJ‰å(lÏ Ðõ‘! ýØ_ÔýX“¤ª°lyy•ƒ.j!ï:$ªŒ¶\°\j¦Û À}ŠÂRЗ0A†}µ#$»¡ÛÜp÷ªüfýú•‘¶ #%ÀÛ:-«Ó·ŸÛ5j»j2ZúÍkï÷ü3M±±ßwƒD³žÁûÏ…Œšü#)ƒ­ùF“© iaáñm1Ø-ƒ¸Ë5üL?mJ?² $>%JL! Òjw‚g7E$,Q“bîŠÛ%Ö¼åiÈ1ÒЋêôž»‘:|C‡¦.Ä6WÐœ“•®J4óêFEªèÀÆm{I ˆ2/yÏRî;¸ºÜ»$Ìô|¤Ü»b&pù̶ù)s@ïJHRr=F½Iþ‰›ó0l8Ü`ð,ív»Ëêâ„“6z9ˆ¥ŒLvÕ‰²ì Ø%Î$$(’¾ÿStÒfê¡‘00}»~„n„†Œ=;O·èw°ˆEAI"" ª—Ÿ¬z¶©Gq1/±ûu»cETÄð¡ÛÉÂHr ™ö¯¬VÌ~WxÍ“ó«¥tN;ÕK!UÓE†1‹1ãvTÎïÉ z!ºV˜<L˜ŽEa§´õ†ôíª‡inÀ;½‚ ™_/­ÛìÃx`¡ŠÒð†tÊðÖç¾°çkxÛš9/Ælèã:ó$g,!`Øé©=f)1é¼ÎŽR¡$i©ü]ñ{c…H5áä\]ÂÃ>,àD¥"O3OÃÆ3YÎú›í”#%[0Ñ#$po4DhÀ¤FÀ;â3"`âPHšŸÊnªÄ°dþ!QPÓSœ‰0|ðÎTS]æôofø#%d3œ¤Ä`$™PƒIÌV\€!8Ã%Œ7iŠí(£Ja-4ØγB#$caé 8 œ@6MSg~5,3g«ac\d¡ƒ.— 5ß»y¡‚ð¹–uÇÌ°·#%“zéery06CÅ°N,%D‘#%h¨Œr¦§™ü  dbišcBã‚6 Ñ‘vEyH÷‹#%àh͉*·ÛF'\kÛÄMª„À A$‡ ƒ ,N‰ÝÜ`ãŠR#")7BuÏ}zèãp;몕¡ˆM“', $jÚf©Ø„$3j °/P`Ê5Qçåþ¦qÆë`}ºYÉ¿ŒŠ‡v‚L!Sœ¢Y ©ºãhj„SºÜ‡"(3Š•æ¶Å~ù„ákŽd3Ç7‰®—†à]`™[‚KÏ}mÙQšÃä¯M»‡NJ`–t[½C‚í:J(7f²”ºÆyª/¹˜S#nìżÌ3Θ”J¥¯4½i Z €c#$ ½-`•‹hl@šh8ï#$ZbÙ×5s#){ÑRã¤#Ê*I·‡™òŠR®,,gÅÉ57wyÏ›¬ÁŒ«ËH<fUß„¢GšÁi‘½ÔË® —fZÄ3åãû"‹–^9Óᇉ$YXß*+ ¢æS4 „×i#%H#$ó!œäg¬$eìÊ3 ìЫåh0(‚k^ ¬â#«ÊE:§"§u×n؃H¤Àì'~tö\bIIeñ2dú:ÇM¬ØÖ–DùG$»h·b¸EC`LBzáƃ-#§#ìƒ:²¤ÛnE-–Eî9€€Ý%Âj„åH«j.·Ä(Ö¦EED&µ+b%7ôjH¶BE÷wʼnžŽ3ŽÛ‘;T_W…+ý¢©mz¦Q u$À“RK Ô#%p%&™2;z%³1a.¼‰X>uYÃÄxW5­¨´MxƒwÙ…›jnªs¼7 ,SìÕ$k¼1úÎi¡ð–-uy繜gXf€‰–ç®u I ÉR£‹ }©¯§›»\Š~EªÃ“,ÏRæíLL1ÙK9ë×G®N#%tšLšŠÄS(ÔnÌHÛ&l‡í¹Žx5-ò陉Râæu³‚À8âÆUÓ7K¬áåß‘ :ì2k½¸)òJPàpêûè,Á™’Ôåš›“HaÒ ƒ®†a‚Šca²ÓM‰#¦©b,(B&’cC¼)ÃSŸº--Î(2Á^ѺÓéi"è®’ 9s¾#);ʲUö’Ã<] sH\­Ê9ÀcÚâ1®®È’“‘Û¿à·Û,iÍ>ã5¿K¢“P½G#$¦*€j®5ožÅ±äÙ %ÓïÂì-ñ†!DºZ:ÙÁntQÓ6‘Â$üoöùJ!í²æ\6E.@ZòXšh?¯'+o;o­0Œ€„#$ÀsdìM^ðk¿>%tÍ« nž8ùùÖ¸cDsM|y’®/O‡!¨ï¦QR§Ðai¬½žm£y¿!`L°º²RkPC6 JbÍ;ËIñÓq!-oŒÁ¬,¥C´:lSCòÌ/ÓÎèÀ娘ˆÐ@m‹˜*ì!ÀÌP•:OGë´´¾±˜ñ‡(It ºpw¹—ÌïÒ]»;uV­á#¤ïȘ}^Üq“#$…³Z«&7€pŒ˜Y#)Œ%JahüŒègÐjòç"¾]•ÒÍéSfÍÑÍ"­¯«Y€kyLôgÈ‹ÎIdx¥:Îé𖦥59ç#†b¾%ÔM#%ïÁ@áƒ='<ᆎ:¢Æ†0ž9¼1âgÓ²cM÷s1Äo¾PÍÁk±4 $´òˆöbîó\U¿ Ä,Žt˜öÃ"Ù¡n${ ÎŽÄHÅ S%…p@¸fƒ8¦òCi¡›üc„6ä„E"GņdŽ o¨a¤‚+Š¥#%“Y5;¤ÛÝyàët÷²Ñ9%1«͘(Á7a†Ù¥»(dÀ`¹˜Ý‹DÌ" #µƒ)Ê3+‚Y,#%#%K‘° ¶‘q\] STŠŠÆ&ê`d”^Ë%3¸h³Ëã|0…Õ/e—o™Ô«ÊÓ1¥±„lˆbßV•\u9º£(в Ú¢ ÙÕ‡È͉£”Dž¤H14Þˆ#HaØßÍFFHH¨§#@ÈÀ#$Èv%‚\¸ê˜˜šÖŠ¨ÕѨ›3€Þà¡P›•#)Œ#%Ìkb‘À#)eç+„3œq»§XÉHg8i¡8ÂB¢ê™*L"ZBÃÀ8¢ ²}IåçÒ|L{(¨4´TDP;qšîEÑ6ååÓ'¤laíä@‡Xhˆ°‰ 5#$¨Aû÷ÿ„·–ËØ>G¡ãìz›Ü=Ë6Rj%×ï+LÔßi©Júg/Ð-#%D±¼’9tæEî /ÄÆôÏe1mÈîºVáÃa7K¼f$ÐÇHÚÝa€Cj —¹$eÈbŽ&ß™,W˳šë–µyUAéµ º29K–Šë¡A7ÐgꫧLJ”QL‚lªG„Màö3]¬ÑðÛ½4ÉÀx4‹Š”µ#´¹DMøÜ™b†‚dçVŠ­Ô>û,’û ­d}åh/¤‰¦Lžî¹wl~ \ÐóìøÏ&~Ö´®¤aÀCŽVŽsJ.€ZõƒˆòX›/®ª­ ¯fãs½5‡Öõ©Õîó7PÞ­ÝÐpÆ#$H_z_|þö¾à#$Hˆúèljï>Jt0Ý"c\ŽWÓd{öÅT°“ëŠm²›Œ\z~x¯ Š¸Bg/­;µÔ^W¥'n‰Ý.Ûi‡oyá* ㉼ƕNÒYƒRÁ½ò9wmy¤„’Q¯$Ôï$ªDØÀay`˜Øì! d…(…K–@„@¨¶XÕš1Q¡Á Q™÷Rd+#$ñN×ë$ί=Üs}õ±Ì&²š24°L0ÑÈ.RIðœ 0ß<ÝG_cëíð ì˜÷ÚkµCæQ:¢ À8„Æ'ta“kÊKUdFwV‹S¤‰JF*Bw332ÌAQXDƒ@å0¯> õ†ÐÊ )ú†Ž#)§¿ÔJ#%å çæ`.‚›ØÃWïk9õ}–sì4£ª£JzÑ›*¼ó!d?qAaŸ/y4(ˆPø“„Úð¨ÆmëNzkÒDårFNþë/X/ËF£UɸÀ¤Ë–q™oµ5?TÌ›rŒ›¹aâ ¦0†° §¶=#)‰~nx/G«p¹&§#)3pfédø]¬ ÙÆ%7r½€?ıQˆ¬Dô ¤ø›Mm›d5ÐÆŒ 1­‚‘è_”¦DõÅ*"{` E%²ÕiFj¢Köed¶£jMQ­o‰­“m^ûn @¹Ò¢€ÞØYV¢H)dˆ7HŽ P"@X M€¯d@v€"ûokqÜD…¯÷h#$°#$c&þì?L›ð5øþ¯o»}­bÒÍ&ŒcXщ¥ïßÝÞLÕXêg 2#$ýÁ‚€Hˆ*$Š¯Ç#%»}¥÷÷Qã`¬óë7!݆=>òt“ÜxÃÓ߸[1F1œ‹à1§˜ 4a¢L"CM£2XVK’ƒaD"ŒTv¢DÈÛƒœƒ#$Š´ˆÐÚPrP¢BfRa ±#)‚í£ $â'Q¿K£Ž3#$4*:éŠ#)»€,-(l¹‰f‘²½}j ØzcØöŽ/4°À„Â|×'Oà}Î,5¶XÐÔæCNï…£gʦ%ccU ºÃ¯¥°v€l )rÄ‚Áˆ#$¡±×‹#%ÄEàb 7ü“´D“jÉRw–¡@$&T• Œa„¢ïih/|žœ:C ™g†¹Ee#$Pf¤¼r!Ex{2›è$Çml”ëÔöoÆÅ!#%ø™>ÄÍÅ÷è²mç·#ž¦ßw!‘+Ž]¶­6Ó¯z†®&â¤;%¼6WËvrr?&\Êᶈl‡ÔS«¾:eOQôzÁöE#%†±A=…†*A ÓMZ•A†[á#%u%ïé!rKïl|²êO¤ò;Wݹ#È çÂÇ¥e­ÊË5$©¦¢cAm[0ØJ„Q³ZQ­R[÷”V®3•Ò@„R‡s¼ßlñj_Ùˆvè®ÔÜwtM½ó7@<îˆqðÜùˆ.K­Ý³Ž#%`!¬E&|ˆÙ=‰Ýà:õSwPˆu¢w©ì}·#$ä,Cƒ³£RIƒŸÓáÓ²('QïÀ}ulÿ  =$4KŽHÇ×,ŸÓÕ‡|ü☣qÇH¤A¸ó½¶’a©»¤îâƒ$ã,%»É`ùÑe®,§DL\³–ø…héñzI¡Ô\#hÚi–Røj'»uM°t‰¨gCi¤iKb Hm‚bÇkÍ<=\”’e>³§|%Ã;í[T†—°€(,´±%Ô bÊ„Ì̲ÑùµvùDMçrœ"1ø} Ì‹fȃä8Vqª:6bßæSXˆ.^Íínµ‡'üKêŸÊÍá$‹¦GNËôÜ´&$C«¯ÎóÉa¸®õݽ@P)eZ·ÉA.¨ú#$B§.ο”ãa.}FÄÈ4“«#%1艂dA¼O0@@#$UR";^¨ôñÁ7ûÏmï»àP#$Wq×âw†(#b wIT›N‘J0½aEF†½g°“£Å‚BBq;L°×}ÏžÛü½,Vì£.ÌgÚ3¤t(p›«‘²QHV„Ö½îa÷l#%Ýîùiäµd#)ÚˆXy½C^Á¯ˆ§`«7eŸÐ‹ÒMtéô÷6ê¸ví¢åU¨v‰Hczì—n^6÷ÙCGK¹Žkä[í*¡à#%Æ…>16‘rbJ;ÕþØM‚í†'_SVkËïQF™1™<W*+$»µØÉe}ÝæA¶+÷g=òóËÞãiÃT¢›!hÅ•äÝd#)ò6Lj6¢°†1–êM©–Öí¿™JFÄü ØD6ŸîËvߟÜxxÊ((õî‰â`|,ÏPxVšALGÎkó_Hõ*tõ‡_XH²+"1F2 Q‰'¸èO0 hxÅ £ €ŒDeŠ™Z,–Y’Ù³jüÿ³ðj¿‹¾ñ¤Q¢ÙQ¶4›JSMªü»oåîò„Ëæ½cè"¨TDÏë„¥­¾Á™—BSÌA,HsHNt"N£ò`¤ÔL#))TS")±Š‰µ–ÑE¨£ilÌ[4iïžyª“V–›RŠ']ˆ„]àä€a~P×þOG-@õ''Γ€$íDM ‚sÔQ¨#}*ið‰SK²ײá 1>A‹{by"À‡`i€p@Êr2Êaˆ’éÅ=ØJ*½Çw‹!I ªNm'ШW®>Y˜’ú)IFw B$„²~7'-=6#)Šî€Z³#%^`:£ód£ê²&"‡4öP A}Š@*Rf6›#)XZ“k›¤µSmF”š¥´ •A‹fÁRæ­¶m&#$ª¦ÎA]”+óê`àJ”ÏH€H°õRR÷jc×Øméªêù$Ruc»Q”ó—0¹{=9¥†ÚcÐòø¨[:gÒ‘°ü”+%3OŠK⤌‰C¬­˜¶Ò”qÎ.9˜ ~ü;»šãùêCf¤<ö6¡ö›écÚ>åóþðÊ#$”d4g…HÓ’#FLJ’4FXÑE%Dkc&ªÚ7ï+~E¯*mA´•¡Sò~RJ T U#)#ð‚ŒB~`é“v @ª‘ A‰‹#)<¨¦‰Ÿ1­ˆX)vêç{çÑë·d´×sˆõ"ÛUæ´[QŠÐZ”* µ6X‹hÕ©¥¬©~©µòôæmògnöÒ«,”4åëÇíTûP¦S?Ë3…ö1š‚ÔA#$e ÆoÎœ’Mþʘdú§!» Ei).賋kŒ¶!´F8•$ TgyŸÝƒ°¢‰&ðBÒÈ]@¡–Ñ>‰+Mƒi¢•5îl5ÊZ7mwvWy¼­uM¢É­ê®J‹y»¬ÍÙ™U×7mnuC5‘e«Ë»¢mwuwv´›*L˜ØÁ¤ÚCEÂD•8)V@¥DŒTm¶Q¤#%6âÄ[Jilš“,«•®­åÕמv¶ñ¨¶Q™•e²–¼›uwn²Æj˦ºMÍN¯‹œ4<Òb2B¶Ä˜ŠÄEË‚ý#Ÿ &¡Ì’pÐäM–%ü íý¦‚´›L¾ug´©­]ù>o5û|g3 tR¡úa1T,½ø˜1©›$™:¡@ÀœÉÇ­[Lànw¡™Ý®K†b.¿ã8eûp#$0}0’ì$œˆöwêž|¶;ÑXE!]7»“y<´œXÛ8ÓÃ;ñÑí^îã~07"ß_ úAi‘‰1êDÐÕ)cS{é[§#)š`SS9¹„c }^FfÌ…:³ë·œA¢Ž+”R3‘x\ëÔ˜Þ9„þkÈÁ%Áæ1‰¢Zmaãéç/ZÙmÝù줗õ‘Ñ™o'ý"Ö±ÐéõIZGJƒ×[ë õCHbyq• -…-Q”4ÅHÇ¿å&{$Õàòí¡|šAïŠÊÒ#Zi^ªŸë7#%R„¾ÍûN¡¾;Ž¢,é‘|þë{öLͤ$~zÄE²=z’IhÉ‹’…#YÂÂ2“p錦-*P`šl.:3m30À5q=iÔ& ›£°5Ñ—@(Bû“'XYŸ7käN% ÝCd¸²&)vCÞ`‘l±(´ãWVW¤Ý=à?ËÃó@°7­Õ ñDĸpZ¦ðËò’ 6Q~ˆ§„ ÛsCãÁ–ȃ·º’ËŽxFêßë¡jIb8j«Ûäe‘R–é^Ó³2#%ƒúdf•,?Š*Ûr½Dæv+åV¶ómA¸(¡“ü4,ŵ9•Ò‚k̃#t[Z…]»Ó‡G~ªÜØáÂ\#)r£}÷C“{QÑ„îr§½{í´X q8p–hæx%þÄÌç†b– ¯-.9¨.Ã,¸ó£Ìa‡rM¶ Ó‹~BœN“xó$ÉôKšžg·é± ‡I¨ñ>iÄ*ü/³Ùl»Ì´4õ]1~Ü „O¿/~+pAÎyR‚¸@±HnG«N9hBÆÚø¤v‡„òG¡è…ÏĺïݶÙ>U­Lä¸+“ 粫hÿËÿûûêÿ/ýýÿù7ÿ?ú~œ?áÿG£þþðÿ‡ýëÿ“Uïÿýÿgú§ðÿ¿þߟþ?ò×þÿû¿Óðúú?özôø¯û´ÿúû¿ã§þï÷ÿŸòÿ…ñÿ‡ðÃÿçú|Ÿé»þ?/ý¿éóôÿëåÿOÛöqÔ¨‚߽Ʊhü@€u@íl€èº•pÿ)ýÐ@ÞM„Áý>ö T#)¢&B€sUs0)*ÿäŸß¥S»±$&f}ýo®Ùm~·tJ#–{rôk¼ »´Ú!®<ÎHTÕ5„ØàEÿ轤ëïß¿je´Ì.l‡ö‰noÝn*j˜=ÑÖµFIÊ:ì+¾H1 ‡0Z¾Wf±‹:× @¡C×ýªÏ#%o™ä§üþ–Lõ3øKþn6^ƒb9u(ðÔ˜ÌiÇú¤Þ* L„‚ÓI }}Xך«Ã”ä’Þøïíü5ÝòÎ$|e­1Ìv±•‘ž#)Ù™þÇ``ÒUÆUTÚ”Ï#%&QFišÈU4Ó.I\Ã#%ŸúphÔ!2ªx´o[R¯­4‰—€ì`G… “³ºÖîN&ËŠ¤ÉÊX!l– ÷4óCMÚŠ;V©³n¬fõ6ÕÆ´8˜Ì‹Xqvak#%í] I˜qx“ý2Í…’îHÁ#a#%pÛÖ=_b°-9¼R6wryÑ$´¡Ý#utFò÷D3ÞiÔ=Ðg=3YGýÍ„e/€ýß 6P®.f,‚Z"fVHÄb×/;bò#$c.Ð6‡í|mÄÎTcæQZ§ðHuo ‘ØáÒ2jǵ.cb¶*t\¾Üº·ýjÄzÿÕ@ÐeAeÉãÏgÍ 5æÞj¦+¹%Y‚sEŠD|i'‰aêõá.áI9C\¯0ÛÍïÜømà kÁ‚ª‹IRŠ]üºoè?hz˜ñÐ,g35V#) yâˆ(RcŸ»n)Ù·S.‰¬$d]®épÅC(ŽÅ€#)ÈÈ ;Α¢üìØÂH b¡$cÖ2âï(wä¸Ëb[ØAöýsÄÌá‹ý’uÊÉ€Ÿ8Ü—ÊÎãb‹&#$œµ¥#~F¥»9Ö‹Î0•ssIVÒ‰#PÞ.%D¡¨*‹VR™[bÔ–¥4ɉ5öݵvÔ¹eH'D@Àˆ'ng¸ö‘ âë"î‹1áTÝo0PE-‹'CØPq)¤¬Qg?ˆ|C˧”±þùgù­ãUh„ßc÷CCþÍêc“Y¯=˜ŒPø W>Ï8Ï/û|6iš·ÆZÈo´ÉÛRÆpÿö_ÙCm¿¤f{عe|1È·Ô{:«SfŠï­Io‘-u#$8cÒ"ñMòiéOuƒö~/·Ò&‡“#9Éf†P4Ò©£Ë3|½’l{ŽÂ}4Xj!>z˵Íãt¬m–è¼e¯«Mëò6[A”¥4­Ùap†„±o£[ìnÍ~½Z@­Ísi6£mËSJŒo·ª6øV·¹ڤіZ mó•\Ä™d,Öì ÍZ#)^ðJcvPääR$8ªbcþ*S÷—ÝÂá$$Fàn1t#)"u:»l\í6)… bSß·i%ôì™BkU¬ŒŒôRѸ(;ˆžû§Õn¸‹˜œ(4’,€HÍãà)_ö^Ãçî£Í“ÐŽÔs}\EðâQ‚ú˜póÃUþ¨‚À ¡µÚŽã³@h’”* @W#)ݶÝe«îh›5&½¾ÍV<¡ H$€ #Šöú½žìœÿ_@‰Ø„xtù@òþw®c#Ûmh‡bÝÑ::1O(4AŸ“Ãêûƒðÿx…Oùõû í&Æw>(YÿÃÄ”"×ýÓn–ŸÇþ©ÿnþ‚©ç10…?û5ÿõ´¯M»Íy…]¡,Yû;ŸÓÿËû6^U÷xzþ»6ì’|×í#‰Í>xð{Ç]Üó#¿ÁÃÚÈ@{òÂÏ?†ñ´‘ì×þº‡Ÿ¿€ÑÿP‰ÙóªyˆqLbøá¥áñ—ŠåÊm‹#ñìÅ×þÕf2ô#G3عBºi³GÿŸPŽ¿2¨•˜ŸéœÖ2’ïÙGí©Gþz-g¥vñ8ª"™Áתáq–s¦ò8‡ÇÙ´éÛD è2²±û¹ËxFá‹Ëa,oÃÂ4N*%¯+iÌ$]ÏNyfαb‰Çz«ø½n»2X¥ë&¸ ²¼>ßefcÓžÆÞGi={í>Ÿø«#À§œgPÛî1ë–«ÂO@uk§øõÿÄc±üÐdcù?_̈ÿñw$S… 㥧Ð
-#<==
-#-----BEGIN PGP SIGNATURE-----\n\niQIcBAABCgAGBQJXucfbAAoJEEm0xnwFJ3qq9HsP/jfAbxzLd6uHMfpZP+GgGJeY\nDx/t+p8lKI3r7VDXLB4UkzljBisNG7/uNhHZ7FB86JVP9WTKOjkRk0a5GyjBHI2K\nNFBxC/cUljiRs+2NA72MPFUkGJWG7LFeYihWhpto5iNDGtyBGxYOn9+wlStixOIP\nVVbRkTxc2Pda+Rwqj3zfPVZHXWxv1kPWhrLth0Y/20Q8CBVa9QyDK6VoXgBY9Kr7\nOhijnsGAX2R+CVfR4iCM5/QpmJClWb6+ER1bPrvyCdTsCzOZZLzvw3+U16YYzrGK\nWj+PE+Lfbj7GizItZSB861BHcl9yGWd0p7MxYoN8tKZljTKksF53r/QXm2JVkUCZ\nl6vY42scP3tThgyBU4OImokEfw5GY1JbaEtzi2sB7bdzcMVgTCWrzozJYqEXXGlm\nPY9Xsqr8p0YZT6T3l4vFAAxoNZ7vBcyxixiOzkygX789/vHurylRBLsH8uSlMt9O\nqRPEV+c/NzzfrxKPlfjvX7Jzzh6Fdd8QzLheKE4skSwbQ9Tb503wral+tPSbKvGp\ncU9PWFlB7W+TiPua+XPg4wkA06lwo0yhDH5wgLme+LkeUaQjHo6+MCweYjZtSPai\n/fAre5LUhHrUpWPogvWSSdq6eK3m76utSSkHCJAj8mvhTWV94A21Rv6dutRvujKo\nCQGdw9yT22XvPzQZ74b4\n=lYLz\n-----END PGP SIGNATURE-----\n
diff --git a/doc/waf-1.9.5 b/doc/waf-1.9.5
new file mode 100755
index 00000000..514ac755
--- /dev/null
+++ b/doc/waf-1.9.5
@@ -0,0 +1,169 @@
+#!/usr/bin/env python
+# encoding: ISO8859-1
+# Thomas Nagy, 2005-2016
+#
+"""
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import os, sys, inspect
+
+VERSION="1.9.5"
+REVISION="0c9b76c74624eda0738007bf6caac806"
+GIT="57ee30760a44c474c292bb67977b9a5d62bae58d"
+INSTALL=''
+C1='#0'
+C2='#/'
+C3='#,'
+cwd = os.getcwd()
+join = os.path.join
+
+
+WAF='waf'
+def b(x):
+ return x
+if sys.hexversion>0x300000f:
+ WAF='waf3'
+ def b(x):
+ return x.encode()
+
+def err(m):
+ print(('\033[91mError: %s\033[0m' % m))
+ sys.exit(1)
+
+def unpack_wafdir(dir, src):
+ f = open(src,'rb')
+ c = 'corrupt archive (%d)'
+ while 1:
+ line = f.readline()
+ if not line: err('run waf-light from a folder containing waflib')
+ if line == b('#==>\n'):
+ txt = f.readline()
+ if not txt: err(c % 1)
+ if f.readline() != b('#<==\n'): err(c % 2)
+ break
+ if not txt: err(c % 3)
+ txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
+
+ import shutil, tarfile
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ try:
+ for x in ('Tools', 'extras'):
+ os.makedirs(join(dir, 'waflib', x))
+ except OSError:
+ err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
+
+ os.chdir(dir)
+ tmp = 't.bz2'
+ t = open(tmp,'wb')
+ try: t.write(txt)
+ finally: t.close()
+
+ try:
+ t = tarfile.open(tmp)
+ except:
+ try:
+ os.system('bunzip2 t.bz2')
+ t = tarfile.open('t')
+ tmp = 't'
+ except:
+ os.chdir(cwd)
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ err("Waf cannot be unpacked, check that bzip2 support is present")
+
+ try:
+ for x in t: t.extract(x)
+ finally:
+ t.close()
+
+ for x in ('Tools', 'extras'):
+ os.chmod(join('waflib',x), 493)
+
+ if sys.hexversion<0x300000f:
+ sys.path = [join(dir, 'waflib')] + sys.path
+ import fixpy2
+ fixpy2.fixdir(dir)
+
+ os.remove(tmp)
+ os.chdir(cwd)
+
+ try: dir = unicode(dir, 'mbcs')
+ except: pass
+ try:
+ from ctypes import windll
+ windll.kernel32.SetFileAttributesW(dir, 2)
+ except:
+ pass
+
+def test(dir):
+ try:
+ os.stat(join(dir, 'waflib'))
+ return os.path.abspath(dir)
+ except OSError:
+ pass
+
+def find_lib():
+ src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
+ base, name = os.path.split(src)
+
+ #devs use $WAFDIR
+ w=test(os.environ.get('WAFDIR', ''))
+ if w: return w
+
+ #waf-light
+ if name.endswith('waf-light'):
+ w = test(base)
+ if w: return w
+ err('waf-light requires waflib -> export WAFDIR=/folder')
+
+ dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
+ for i in (INSTALL,'/usr','/usr/local','/opt'):
+ w = test(i + '/lib/' + dirname)
+ if w: return w
+
+ #waf-local
+ dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
+ w = test(dir)
+ if w: return w
+
+ #unpack
+ unpack_wafdir(dir, src)
+ return dir
+
+wafdir = find_lib()
+sys.path.insert(0, wafdir)
+
+if __name__ == '__main__':
+
+ from waflib import Scripting
+ Scripting.waf_entry_point(cwd, VERSION, wafdir)
+
+#==>
+#BZh91AY&SYqpF3’ÿÿÿ°#,Êÿÿÿÿÿÿÿÿÿÿÿ$‰Má ð#,E„(aüü÷=n„#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,>û7Ö>ÍÔÌë¡;lÖ†#,wuÉwÎîÚ6±õ˾ï^)OjV›î˾›—·wÙΗ;vØ.dù½õ}÷·Û½ºëÈK·R×DîÜòÞc6v5ëB‡­íîÎwݯ}šv·Ó¥ß:Þs!­›šÞú}õî‡{ÖÒ«½™Í½îû··†ƒëÞÝñ¶‰}÷ßwƒÞÇ3zeß}®#,#,#,Ð#/#,|ØÐÇ¥#,'Ý€x³eÌ:ö÷l½ÝÞǪr÷]®6ƒM#,Pu×;>¶N•4#,#,Ð4i­u¡Jíš#0˜B‚¡)RvÐWc[H¡BI#,%QB…#/ìA½ô¼÷½Ü}°ç“Þ¸ÏZ;ž«{t xt©Q¶ZÅ+nµ»wÛz”U«#,m«Ï·»ÑFíï]®í×½ë«ìÉr÷nÛwzz÷—Ïi:w{¼7»36ﻲÜ7{Î^Ð4yÍY³Z@zÝ”+:ÀVRîíÍw{͵Ýídz»¹¶ë¥v×¼èõ†ŠÄ‘ osw—¼#/#0#,••W°5N„èP14¢”·½½OKšÛ&ö#/(û{Êóï·ÞùëÞí÷°òt@}zp”]mkÖu¾ƒ±€w×(Ø#/ôÞ-¦žÞï@gw½†ñÜèJŸ]Þ:}õ{kzç¬{ÝÔ#0”ß{Ǫ¥÷Úö^.Ëí Îû¥—Œû8Ùm-ºí·j]]7·+¨¶Ùí÷{Þå¹·ÓWžœwQ}êë>Ë&Ý]õÂê½µ'ÓíŽO‰ÞÞÙOí=íÔí»®9ióÔ½Ît®jîgÏ›ëï{¼Ù¶Ýw[}0îÙÜX÷»­ë^ÛOq½.ÈSvÛ.iÉ¡²§wg¼Ì#,{W/yÞƒìôÓîð#/îÔ¢”¢P#0´›#0ÐxWh9¡õ׬ÕíÝÛܦåjéÕ°YŒží™Ðõ^¥êí#0Ý.x{°KÞ½¶Á¡îíµ5îËÀ#,;zêy#,{«—#»ÎêãœæÎ=ìÛ¶wgKe³œlyÈî(íÍ]i ºk kâtôyzî){ºí95ΈC­C·ÙÇÝ^yõëÞÁðV¸ïU—¶oC¤òõ)í¤kÁO6övaϯ÷³ë¾³Þö×/o¦_ÅÈ÷}»Ã“Û»»mwÉ×Þ×>×iµvï>éó+[´f0>‰iô'sŒ®»uë`™´AÎ<XïZ®»]›ÞóÝ^9žÄ#,€ëVÙ#,½½e=»·xèÓhÁݳ·§}Íõ÷#/o®Ûkzw½+}Ø=·^ó)ê›Tù݆÷¾n¸ôu¯¤¨#,#,•µz:xŠë6ïXsÜÖ#0P!Ý¢æº÷­—p/`7gÐ:z ìo¶÷uÐ0#, œÕö¶úšÞ[#,}Ø­ØÒ¨+¬õ»Þînìaé®[öíÜÕÆ·ºÜ*õÞôÍ®vÕÛ;£’±)%í·tÛ$ÖS®îg½Ô÷Œ»SnÛ‰ë]²oMÛync#/|ãÇa÷ÎË»²¹·ÞÏ‚>ÆÙ†F·3‡6n«¶åGж¾í÷£ë˾û¨}¶îív—›_{¾­¾O½Žù—ðÓD#,@#,hÂ4#,dÉ©…!MG’=O(µ¨=Aé=2‚S@„ @&1i ©ìŒB&54ÚA ÚƒF@#,#,#,#,#,$" ‘L€TýOMTýOÕOMˆ¦=Êz˜Œ†Â`ƒ#,“Õ(¢AÚ¦12'¢ž˜DÓÔÈz#/ M4ƒ@#,#,#,#,#,#,"H4Ðh#,™4#,˜4‡ #,§¡’e£@Ñ #,#,#,#,$ÔD&€@&M4ѤƅSò§äôžªzE?#ÕGêe==1Iå=M#,#,#,ÿõö_çÕZq ÿRÕÍ[?b«]Ԧȓêª×p6dÑ£ ±LUEŠ#0x Öcïö~sù¸ÿ”~qDIRMQv–ÜV.ægÑss…‡˜ª¬QŒ!òP‰þf¸ŽÃ Ð †$ /#,xvd;ÞçºÈørówR­#0쨰’RœM^.+Æ/]ÔÕ§|¯m('ݺ¾&âñˆŒI)wWUi«V6ÛIk[¤E$ P¸EU* ‚º-`I#,)™I ®@$1‚‹xD r $ zâ´@ Š@@ˆU# 2*^ ª"d­UL†1™±¦f–D ”Fi4lÍDL•–2"ÊQ¥ÛI°ÅLÉ4Œš¦¢¨Ða¤°SHf061’Ó( F¥#,i‹*mH¤Ù-¢))e¥)µ e¤ (& £61­’¨%¢4¦™„ÔŒ”4SJ‘#)±´l&´­´¶Õ!Lµ Ø“Fbd„Ñe’´ÚÑšZ©)52T¶Á6U2jZ™i,˜™%EFÊÙ‹%È£D‰&ÑJkhØ`…‚Å!¦c4¨ˆ5)£#,M#2ŒPÈŒ’l6JÒK"³(d HH†’šVhɉ£aˆ¥‹RTX Ô0©²A5)L#/ ¤ÈIJhC11RQ¤KF’i$ÒbH±dfSS¦@ج–± °Ì©&1#0lk&VI&*6(#/D”D% )±A!0”1’R,²fdÔjeDFЛ%HI0"Rl„IJÁˆ1e™ƒfÒF&‰‚6 –$ˆ¥°”$ͲÌde,–1²A¦™bˆMŠK$lQ*BfAÊX(ÐT³ X“BT’ÊL Æ£IšEE ŒÓDMT#@„Ë#Idb6JL‰ še#*4fk)˜¨6†S&I”Œh&„†d¡1@$Ê™šÓÛ)´É35"‘#0AšÊZaY"ÀP’D”D–,›6FÓ4kÈ)3$d¥‘6m0mˆÁLÉCfM)’†3e4EJ$¢ÍK)I”RFLI‘JɢɦÊA@Š,V RI‰$Êj6M"FÆ„“Y¤Ãh4lf “!,™FH£LÈbH%›%2)DBZBj›6-¡2 II£l‚e²)Ècc ”T‘Q`•Œ¥&ÆhhI¤¢™K"%0Å,±¦X#/F‘(Ê2Ñ5jmkFÄ’Bi”R™(¤™‘´%±)f1I‘”Í,©f‚ÔÍ `”RÄB–2‘¤)¥¯â6º2+HHJ”F[¨ÚÆÑQŠ“&YQ#/’hÐÒÐmÙ&£k"0´ÉˆdÙL©#YLLÍ3Y5cJk%!hÁJ,J#hX,™)iL™3dØ,YVš4ÙB+eHªU+0¦ÍŒÆRÖÅ‘Ù4ÊÈ•%™µ’¬¶R”Ó#/¬ŠÈŠÑ›KZJe6ÆÛ¶ƵTm´&5%Tm6M´M!TPT*űa›F¶l˜XŠ©#0PJ)ÑFÔ¢iµ+¶5"V1i %´’mkfÛiبHL¦TÕ“LÙ&–‘E$mª[J£ËM,¦ÓY¥6©¦#¥BÊ$›if©dTBÚ[2dÖXÐÍ2É­-K!k4ÄAC*4‘Æ(ØÀš01 H† Ð˜ÀËQjIQ†R¨¬“M&M%@)³DQR#0Ë#"Ê$…#0J6S4 T$¦ƒE¢¦HDRÓRb1¶,Í4"i!€ŠŒ˜ÃL$d´X! !&ÁBlQ¢K“4³$-L¬Á”RcL&T”•†J3*‚Sf’J3#0$) Y›DE3hØƢŠ²lÐ…-4…FC"4hÓ)¢JJYKBd¬a”Y$± h¦‹TlÖŒÐj*Yd٤ͥ“hª4  ’ÆAd’ÂT•¤T¦˜²AF[$¦Æ Û0©¤ÒXÍ%iFa¥6™µf34ÓS4‘ Œ¨" •4ÁƳBŬÒl”Ä11’’6£T&£Q¨ÑFØŠŒËa¤ÃI%#0IŠQlQ`M ¬²É²RA±hÈY)f’Ì hØH¶-Q&Š,µE‚"-%Q°Q†!aŒU)BÚ-”³,…F‰"©‘¨"-"ˆÊm¬Š”V+0MLʦ2¤¢J–%%ZƒmJ,•Œ)Š™¢Ù)+Z”Å%)¶CY,Z%›2 Ê‹ ‹LRQ’#²a#/FIA­™0b›%)³6&"•EQ™Y5’)4“QAŠ2š¤¶-”“E’ÑERejPQiM©MQ&£Uc)‹I­‘#,ÚcRQb‘JfÈ̉*DPlÍ™­b©4[F¤´Ó[C+b¨Ô…#/h¦k4ÛE£QcV2[Rde(²²±6£‹2ÆÔZT°¦ÙFXÔĤƒeFÅ•KclYš“¤µŠM²™š£Fц‘QIEm¶m¶šY,*a1&ÊM,©&Ô†$•b´[bÓ5mÖ³i6i–¶"ÉmMM²Ôi¶‰FQ@hÈÚIŒ#/eLÔFË"¢#/D˜’L¤™!lšÑd`ûüpÿ±þÿóU°þÔþ·¿ØÃGVjÿ©*«Ü†©\L·!– ?Za#0A`ªÂÊ`‰Sïp†g÷uI„A#0¥¦?õë‹.)ýÞòˆhó%qÿmkˆR (1:7"T°\0RH9"Mí¨27üZÚ%Ç&ü™ˆÿV ÏþŸuú‰%#0XåBV¤VP´}Zê“7¯Ra"‡|Ý°ÆôJͶ O[¬FºŠ¶…ÛRRM8nైþÉÜF£ŒœDHXTÓerµÇàW.飮ÉÞwA/–øñwßVKÖåv²~“©»¶¸7­ÇÏk˜«å_/EsoEÍÊ‹ "ÂfíºãD¤D…æ¡WRyÝ°Ëå¹²3zÜÒPX“6¥ˆ¬xâsŠªzoÿ…)ýö#016VŽ—™ªú»¾¦Ò;¨_‹·‹É¢#,X¾¯ö]áĦ\mâب¦¦Ð¤Ruß%cýndŠcJïVIh7DÇ#0!‡×¦˜À°gDØxº!l#0¡‚º4»ñ+ÇRÙúŒ!¤F,ë϶$¿÷¬a +Pì]ÄSñëËÂ+œè»›™)#0(¶H,X¨¾é}³  ʨŠOUQlö¤ñC#0ÍÚH1±ŒdqÃŽ´õÒä;÷©›\7¾¹x#0MS.‰mAF#i)&Kk±ª4Š4dƒ{œlË ÔÁžîb¬9bÜZ¨¥a7"rpaæ2N»¥æCVëºÿ^þîòù^ÔbÓâë¶F¼•Î—|}&ðE‚¾ê)ó©¯ñ8Ó4ÿ¡éƒø+&œh+û¨¦n“ž¸µ‚ #Š†À2õA­Bÿ¼;È›Éÿ9`Úœ¾”ææ+ƒf²Ù"aYHQôUtÞçS¦¦ïÏÏÍ5ºš%¼Ö\g²Ë ›ÓŠ:êd|>ÌîÀäd×ÂxxTt­´¨kŽÊõ% ðª‹>ŽñyWRÏ:í[p£“5B¶ ¡c„#0`…„dN‰”ÆIš}Ö¬£³z ]bE#0бá’íʱþÊ-“±0””ËCºÊPMŽïÆçænEæßlMùó”ÂH#/~Z‡Cf8¯µ—o~”ã+¬,‰·6ïóÕ÷y×Ã~ƒ¨d˜É/»®¢‘ŽOmŒ0éety"60ãFK”/üŽ7ì"žtw#˜”m¦Ÿ»¤£²vß^߉»ôW󮦋!bŒkÆæ’Æñclk_éþ×{1§é;&·ÂãAëlc÷=ß®ƒ¦j ¤%!V»ó~}ÔQd#/)¨¤ùèFX pqŠ3Õ`gfxY¦F#ÕË–梬^wåÞûÞte(ØÖMŸ7rÅoÛšëåØü½»aÓî}]·oŽúyä<ÅŠÅ“_cîo¨,–ünÂ5ôîÒ}:û¯Qž^y¿n´X2sRê’š`o¥Ï|Øýµ™(0ˆÄWJÚÍóBa&µÉ¹éKB\b±•@nït-$­è«h0ZJÒ¡l5dÂÈ¢ZP‚ÒBNÚ€qÖ”Ý ¼U ¾J)”©.ªÚUÛº¬R7ïמC.ýõnï$,îí»,"£sÊíM˜j…¦ZH##0uGJ» |¨¤:0¦M2Ò]5„²ór•S¶Š?‘”³‡ –ÅQŠœûjO41(Ü ©äûò ¡ì¢„õ_<bÃ܉GU”Ûrï‹æj°ã‚Qý®{çˆt°µNìUuâýxaÄ}QQy45Z5Ú” U%%0¢diKñ÷)~í=ÛSÔ FB#«ŸûYÒû.š>ÏTņÛá]Åë® ÔÑ)#0H‹@§&”~­èü<¹ß¦èàQ]ÉËϯ̓H2CF…lVÚTSo=²’\‰cr#wT#¯ØËa¢È˜Ù½AjC?<7wÆcK‹õÜÈš`£ÍÃOöktïªv(~š®œª¾ŒN/Š‰£GP§Tþ.wÏÞðlÀlšaŸeÖ붼£é/–8ä®fRÌq=öÆ$ñëÁµCݺ„r‰Ê=ײ–’$;â†q²ŽS™ã·mH7Œ iKx6qÑHYó¡Úýäؼ™Bõ:Òd0³œãË:vÕ};ÓCYÞ£é»3áï鶜­×X5£5I”¹&?J®åE·*§èÐ:a|œ•êö’Tƒ‡-jÃdkÛ&23쮸3³'©¶d±ÍÛô_ü}¹4 ‡#0ïºg‡fq„gb^ç„ùçÅ—"œpã=ºÑ[÷Bac:äC,dMǪf(=²ͯ7¤vˆüš+²ÅIÜ„zúe1¦Å4è=Fj/T÷D¢~Hq7á—ëµÍ±‰{òC؇o{³áÇI1ÐõUñç2N,ïΞ8)@èÐÄâŸ9Ê°©ÜBÖ³-ä°‹tæbGOÒCùi© :Îz<:öI^Ú¯ÞãÌe;Ì4Ï0<u/ëâŸFé==™ËÇõù_ŽÈI$‚±Cïq?HB?:am1Íû´omîQ5ya‰Ü "¸‚Qƒ…ûN9âjì›È‹„Üi_d#0?q»ÚæaZ<þ1"4ÀÙÕ–f÷fjLÝ:Và‘äͳO…“Å·#0¡­h<1H§˜#†¦…z~z>wÛžöõnlæjûé¸×¦fCd5DTÝ'Ý'Ï·Ÿ§Êüq:wÞ0ä€@î7DÁfHÝJb#0N$QK︅ÍÞ÷PKôzêmEFy4,qì¹~m UKTW¢ª#0(C•!¦¿‘Ÿ¯üMÍ!ËèÅžð‹ÔU&ªu”$1BApTôU¢!uP.¨_{="}nm~…W<"}-Z‘ûN_µöç P>ÝÒÉ?¶Õô[¸K¼G ³­+¼Þä\ _o桞ȷ5UÕÛ­èÁµ¬Ðk&0ŸLµ»Öf>«*ÏùÎÃÝ¥,ªEG£B:ÔŸJtpŒpgwKF}óMN|N \=¥ü CáW3ö’>"#/Œªû*Çr—ò‘<Ö½z#/4™(ú¨¤A_½0;vfÑEØÅXØÓbm&ˆÎ²ß[-‹á)ønhÇÏ5OžýyÁÄdA—Ž½pÁ†8Ìm®ÑD/QeÛ7ë»e1å÷M=<Fúˆn:9ñàr¹»jkA@îíu2•¹^ ŒT]~º–Ç é;wù=wÞÎ4;#06Ž”Å"“3Š»ÏèU&KïªGƒ5¸S×»k˜÷Ñ{bÈ°ƒèI±ÛZFf"(¨²˜Q:ýÒ‚ÛzÕRˆÍ:âÞXè‡õqÓn!­és9÷ãV)}h–ª©ŸEËN+É°g7TñœéÒ>ªwx¸‡ï¼®™wx²vhˆñˆ±'D¦½Y°DQ0ïgÁ…ØíéÇNuVn´yu¢#0h]ÚD´ñaK–RŠÄ:&Ù“sŒwôм¶Ù#0ã[E¹$$?ïWçÅ8h^é> ýÎY]LrtÈç¿ù 3ÎsËòŸ©U©#PüÞkÏ“êàÓùÅ֜ݰ§­y±º÷DcÙêg†I!”¸pkhÖñè'ÿ¬ízw^Û¼ëž9Œ «Ø&sαñ­…ÒCìÞ>o—Ûø•Na9zLy#¦ Ä_‰¡í>ÍX(E`ÅT#/™~ÔÖõ^FÆ*w_¥çßótÒ ”å"1ýU<Ò‚¶¯?3r(Œùžõ¾e‚ñÂ,ÎiH¢¬àPk¦¿–L‹¡©¸xÞiùûÎŒþÎ[Ñûx#`²DÜ\¦¼¹+#0åÛÛ€¸½ÏåR„¥y­„Ù_N¯ #09~ Þ‹WçqÕÝ&”É3ù<åÉKκUU¸ëÑgi†Ò™o-8y=öÈõ„Ä/±ZâÝŽýZ})·‘|ÿg´1Ÿ‡œÍòàè|ËU8°ÿ§lÔÂlj™²ïcÌs.a껧®ÿd|µleÜ5Ýþ‹•¤þ‘꿆œè¶ßh×_R2¸÷ñ‹¾$‚ÕF[µ2ÛñPbˆ,zðeŸÇú´ÛÝŠ'Ðüx«‚ÎÑ÷8NÍ‹,Nh°Î•wv!¶_Ò¯œöíϲ‹Ã²¼kÅ`© pŸ¨ÕQ¯;Ø}È &5[ s==œoÂg–F¢lÉŸ£Û}4ª"‡ÍP_Jì¤Ðm¦5P•XÔ±DÆâá4ØSòaå½s‚éHÒ"S‘])§ûùÕT91öC?¦©–9¢ÙŸèª·†subh4”ƒ.ÅSqç(’r˜ª)ç&<ùAV˜Ÿœ÷Ø÷Å|LŽ"<lÉæZ_g…W²Mþà Åל½~—ÝÇÊûïgSæy;Žî£òëaØåPô祃¯O— 3[YÁ¤-™g¥Ÿƒ=.q½µ‰^I_#0¥ÖÍ/†˜-Ju’MæzTasm2L¡7mßÍP!‘{{*ÍÞ7D­w¹S ZþÚúðyQÚÌ‘Q‹ŸÉÓ6õJª‰K«¢°l¥qpçÖƒ•@«ðW‚$¢}S°P‡d(ÁOV "‘â¡Qš-ètrª!Æ©,HnÕG¡}Ûä\Ž/r¶åi©}2y @3øÝÍ–h §žì Œ¹#/xL” ûâ&¼Cîç4Ç£°ˆ!~²µ¼„!_®Z>Í_HŇéPÐ/ÈÓ®(8¦ñ_rŒä¹û3‰Ö¥Cd®t1])ÏÙ#ý\Fs0cß.fF³ãºc1ÇRª…«îéa€UDYéON¡\óSߊ-Ç×R÷ÅßÜh,Ós1l@éEùê~_]Hìê12=Ϩ†%Å€R1}ÀaR‹w‘~PL®ýq0—'LÕZoÞë³ö@l€Xøo3­®)wÒÚÍJ{ª§Tfô•ÚÕ  ì™K¾Ôs¯Ã»m´&à†ÉºbèµF`L!@ˆÅŠÆ v^i¼"ã-ÅP0¹(õfŽiLÅ^q¢e…}­O™Ïà3¢0YµUDÝ%ú±`«!zÕtªba¢“V’e¡g½„ nϾNÕÚÜAnèýé⟺%½ak,õeÓÄ6&YÃŒ?A1¯^µ˜[·prþvW‘iÌJ3p>»Õß\NF8eÕÙ^Úð²˜hÓWt‹:5.»¨J>Ы^6õa™•#/&Àü[ÂSË.SˆÌjŠ‰¯äL• <ƒQ9©ÕÆÙäÖk+­ˆøYÃLLkQ(6H(7‰<÷ö|áWÅñ‚ꔬ©«ž<ÝÛ›Äír)qÕw']eçà±B*¬Q kLFéoï÷øYé¦~³Ûíø —QQŒÔ°íébIí*I'„]l^S¢Ð`ŸTÿòEÈI™Ã÷sÕ£\0Ø~ãö€&€ ÙO-çiÂÎñ´{qX= ”((ŽU§gû>)2rŒíQœöçQä‰_«‡ LÿƒdœÞñ9Øg^Ý*ñ ¢§šPüPin°š-íÌT‘åouDvòŒ©Ll›7¾JÝ-°í;:¡!šÌWXÈ5Ѿ‘§ldiªÍÜø«7º™¦’—6PÀîgá–¸iAºD†Àîù¸¥Ò³»˜^ü=ìû+­ ÊºzÇð†¡J`} ‹½q¶,ò Qä@‡®µ‚úZGû[UàúÁ­b?>ó\7Œ2ß„Ý­‘»ñF…’0/#0ªîõXüد…… ãVë,€|35ü±¯573jsy ø«{[òn#/ñG}ªËþMÑ/q.!Uëk1)™ múš®]:Æâú:Ç?%NóÊò06îTA³¸UEÏb¼*g•;Þó†§ÿˆ¶Tq(Æ.©‘Š‘WÐWQX²|ž¾Ì„šv³¤‡Åö½*ÖËðü³:ì‰nFá×vÞÇÃâí«¸Þ0¹šDéb#;Ì÷x<§n#/_”2© ­ªµB+½:kçy'ŠS#¼r‰d­í1Ö²ÙÕ¼]Э¤¤ÊX}¥š“néJÍ¿n1SÛ’Hú­ `7ç®»€m¡u³Éüšå¥ÖO»qñ1¢°>?ªƤî‡ßˆ ßÇN^p¤o—ûTÚ)•êÃ;{o|fȬšQm7§ïÍíéï׋ºZe§Ï›¤B#/âº<¦îï.è‡KŽ$ÛTÖ¥0£2‚[qò1@(`¹RNPuÛ‚ª0úZNtMeuf{v¿#˜¼Š…ÜW£ËËi<)È(r‡¹¤dãžÆ'À‘ðƒcQ©ªP÷¾)q—)¶Óâ×be+7Y=w¹ÙŸ#,$¶ÃÒ ž'Á·>ÇEËïåã®;m2ÿ$ÛÇîàÛ¦bÐÁ‘H–Š‡¢Ç¯‘QD´5wX¼—ÚÒí±YëF\ îÙcAÅxº5b;Õ¤¹ŒR$gÇSvCæ˜éqÓ Uwd×m»v|ù„”œÒÁnÒÑzÌŽêâ7m ÌÄèn·oMn7lßc uÔˆO¬O倳§³Eá4@•E(¡LLƒðVp1#³j¶ƒ+ô¸0ølæÐàÃú‘K› Öø‡aÎÄÇ%/¬·úm”$+U)ƳÃi®6ü}×À¡O¦dŠ@ˆÀ÷Ð%ÿÊv¶ðfÐHÐ9wÑ;EÝñ³ßriíåÿýúéÇ—UîÏýäg“¬˜ÜÛf@§]H 1 Rl ó ññÉ/$ÍÓÖ³ÓZãåÛÚ_n?63…ªp·–”0³32\×Mÿâ \rx®U1í¾á×›˜êí‡#0G?Aô¬çÊey?Ivôñc¬ð%^pÃÛ°ö3æ|·ÍKçìÏë›è#ª?g=Kmk·M˜-£Z’߬µë^£•ÅgֱʒaVlE³ã¬/÷ùd­ŠãÒiµD"[¼èÖÛ>4χ& ’$<â^(¬g—ß”ºˆ¹ÜÔ|ñ›ñã)Z£L”8ÕÌ:ñ '#/#uX¡¶(:xÅE‘j9*¹ÕÝ­wºÂ/¨H0#0Q–o¡Ëk©6³f…9èdó¸6J7¹_°,ªWXÞD%­˜8Œ¥Ø#/ÂéÓ[œÅl,îw#/Nš½×x±˜Ô7rx"g¤?#/^Ëòùöæî#9©ß8Ï»ûÃô'üfžÚ<ïËÙÿVþßw¿µÆˆ½©œ8#0žFõ{É”2ó¥Ê3Ö D¡à¨¼£¶IôŠ¯‰#/ón¬ùwüxíñ#,M›ÿkìK‡Ê‘ßîÿF#,%#,%"A/ôB®žD 6©Ê0]Þ*hµ–IV©¤¥½úÁHqt"<à#/2ðâ4g®iT»&æ;|ú<~’±ËÏhÔ#,OäTCg^Ÿ5Тžo£F—rÔb.´„ÝTHÑÈaNÐÎ×=Çñds#,L«5#/`B$`,€?«úwsòýû…ß³¾¸ßSû¹_/„ïã7“Ü{çÁ'Å bÚ{›L ÿÆØ“‚7#üÚ¬rài ï’"QâŸÎòÁŸöôÎoÐÕÉ:¢)‘{<êˆ`I ž£ÕIÒ1W~•j…ë@òâL°i/”@x¹ãZGUïä°šíômé¥ÿ Z¡ë„÷•ZÂz‰ç‡ö>d³GÖsl˜¸:=!E”P PE„ (¼z<£ò4‘áî×ßÀäŒßM*»À¾F0Ô Ú­` <4^@H . #, ^úR4sAâ#,wÆ¿#,÷BÚhôÇÛêÏ\³Óf¿ãú#,ÿŸ$>$|ñ5„aznà±ÍÿOÞAц6•‡¶½ªeŠÃá<ÐÖ¿¯ü3÷ý)†íZÿ|¸6#/»5Ïí–ÄLÑx:c-5]Q÷6;}}úN™ÿleøõ»Ùäx»mV`?²þ>šÔÌ^Ì°×RW{°"qÌ!Ÿ4iúÜÀY:ÇðÄÕ­gmÓÌ9=Ù´Uªº|#øÁøÛ°ŒÏ§“[øÚ_¯•ÎB@ÐÕ¼%2²áh²w4#/¾¿G·M=}Ô]¹M@H¸–È;Ü|×ÜÌn¡fŸ;y_ƒ¥fB°¥’?iÌÙ¯×1Ïto[%úYõ¿ÙË:Œ¾™jÅ„+sÁå.¡`¬?Ø»o@¥ê+ êìuuÓè’]|P00€VP…Ue{8ZˆRZµZÀ:<ñúDµf®¿˜y+¥Î°›Kôü¸ÅݶØI­ýÚ/=!­êŠ¯ædJ{(Z÷‘þO–öH˜c+¯>ÔJ(Ûb2xò`w‚ý³ì^s†fòM(Â3§Ñkù?{û°¥Æ˜vдãï±åý8†L!ož3+D«i%1áxª„×öFv`ÍNöG’E‹¿žC'@PŠ«ÅNTG„Тܧ&iïÆÈÎ~ß?mLÀ+ûþŸ#0jáT‚(Ž¢v\þ=˘Iåê¬ïC‘±¨ÀakÚ›;‡VÚÑ&¦ÃgÚ*˜Ïß<bÂÙ‡ºò„ ù¢³GíµáB+².àŽý‘ÐàºBbb/˜ÚØ ¤­Ï£µé·ó¼žäi6,##/=¹ûpnµé“Hå[=ú¸@c#0ÚÆŒÉmo"õ(ò¶œ§GöSšwk9ÇØÄaúó&õ¨û8¾z}¾ Ï'~„ÙþyØM»(gI¦‡æª—è –}ëé¿ëa<þ|ZÚáå}Ç4ÓŠ÷çkô<L¼“è¹cY¤Ü36¿H‰5ï|üÅö~‡Nš¾è;„‹›£#§-‹DóE2@·—!ÞÿK‹ºwi†Ã"‰zÏÙøÝ^DF ÉÞ<͹Å7½ kø,µj˜ÙwÄ°ÒMv&…{¸a‘ßü\$6œ45Jªi£ZFêZ£Qº!Ÿ»žü~¬Lüj‚#/ 9|BghßêÔ„%ôˆc„Ùº¸¨L¬{ã Ä]­å«É#\­uFŽEh†1ÝûgMñ7›¯î{ºZåq.F$D¦÷;Gi£ú6ÞÇúÄ’p;|;ðPe`ttGŠ«0<#0®Øpt¢™‚¡Oì}Wßð¿pßWÕû®†HTXB0,BEä‡c\I³Â­P™h6}òr»ƒi3LgÛ¯ª^ͱÓø¾Ü+kèmi¥¾n@±ÙÉÒ‚LU°íî¸õ¬CÔgøÄ:8›ÙÚ¡ÙîyÎôf"™X†t‘6–³­í½wø}æõ∆m¤ú#/Ä×Tê™ ¹Ld3êXÕÔã¾mdb!«ËÜøÞ^‘ïi1·“^óÓÁìtšPƹåjé<æ}×PhtûZ_ÉL˜=±Ž¯=´]ãÊŒH‹EYI³>ƒyÄé籊Ê<¶°oñþ?å#,cja„6t$$À€êÅ@¸øéŠr| ‡ó# Šò^sz¸çNÐ0ç,aèpŠl«ØÍ•è¥#,°B(Ý@‚à @M‡ßüØ"ƒû—OËm¾"•ÒøGº|æûk‰ñŠ¾ðt¢§ˆsÌ·9@kn±¤J ûûòg¼Ñƒó[¢#®«3ÎeÁÖª,‚¤êP!ËÝ@á(ç¬5؇Ž†C±ÒÍ:®Ä2›³¾“jÜB÷/V;hÎ/×Î~Âë]9†ågܾì´ÈÔ¼xï%`Ô½ÇǶ»ž¡´ãÙŽÄ݆?#0>Çšps½Jž#0ö¶Ì™²°+a%IE +QÉ–÷)8¹`¬Ù­…iž¿y³“<g¥¥¼ƒ„§tиøYE‡•òå›r.Q×Éü#ƒÐÌꆄÊJ[·’¶6 tb!ÍåÅÜz|ùË'<4C†w:BñwœÍV¦”n¶‰CÕeÆ©ñÕô¸Tü¯„H6_Ç]gÎp–:×­ôcvy¹ F9v©½Œ;•±·¢5nu'P ½dÜÛd3j]1K¢è%2:­#0k/OLU—6ú²‹•ä÷îã>ÿy ¶ê@&›K0?áï2eÌùÙ-WWùï!yþ²D³è——Iî–¥a+GĸÌÍ*€ pÜù™¿›•^šTr–×%±Á“Їúlß…iŸs#/Ž8°¶p¾(²Gº¢ñå\òa{µÝ狵ƧŽFb",st.mËFÅ'Õçê{~äâ¼#02¾ºzëÂùÔ¯Käßê¸A8f#0C:fHÝÓÊä7—»Ííåê’nÏHõg® M:!q)Di) qN„2p°a¼† Ùððï®y›«ÏœVÉF†ÖHØv½ªàÔèôrBi»§ 3ódè±Øì^ÕQGá.Á¹TFËNÍ Ðš±ü¤’…Å+c`X[!Š½±åuÍÛeå7“ïL;UCšSós¬ÙPÁÂI)·fp/2uAO7‘&#%#,À{¡ÔWN-­0B†TɃé #0DùW}« a‹„C&uΉ6¯6Gì1“H­þ’ojœ]vþQ¿¯¤à¥—fpÏ+úìÉ^yÈÕµs½ôÛ fLןo¶Õœ?XÇ­#~Œ×3Á·¾&‡cZÆõ?¹©‰£C5œ*јτš乺ͱÖÌRÐå&☦À:+Š:™Á.ÅT4nL< ͤP©PTˆi¨u¸ÑŒ+`0ÃáhÐCˆ3#,ÑZJ¸!3E ÎXÛd1‘,䉇1>»ÒO——ã¿ ÆýuÈ£gWÔ¶á‹x®š½bÕ÷•çÏ[rئE©SQWÀ4”hm%bá€ìÆ2P1‰dëk–¾VæñôZê[dÛcfÆï™åñêÝ>x8ÌîFÿn 8¿¬ÀVÉÇ\?qËEŒl`Í:Ž“Ë)g¨F·‹ÙüÆP¥¤‚µ‘ú0ñ ÷gÃcÙõ°WPª7žƒ¡<;#/s°üNί¥ý§ª|ú“^º€Òáú…®„éŽÑp|`j^í¸æÇo[D#0xªu–¥]NK2ÏòÐy]äæºó]ƒ\Ë»B}OilPÅ:¢WQO2w°&v>W{Șp\p¢ZKn2´EdliIh7$—Ö¦#/}[ž?¦¤Õf+Fª%.éTÍîR0¸Ð³Þ¡;µ\q}$‰kT}úgåP~u®1±c›>c…¨Aa›$H#ÃÖÀIñÚ(Ò¯+ "ÍõÊQUwÈ•L¿½U0„é©—’vlŽwÚêF0µ'††H“6ßÉo”+wÖÒ5ƒ<ärÿgŽ·²loîË¿¦ã5~Ý#/f|L¤’Pq5ÑÒÚ.vi¤¥sºp¤ãü´÷Kq>³˜„A#/ÊD8êØÑ‚¬ìz³¦ ž?Žð㈙zX¸}…f‰VMc¡ºêtì«ðšÑS*³Hƒãd E­êÚ¢0D gæ=§ôàá8äm…›þœimð–¬´<¯À«ŠRGœ5…E;%#?ò³ŒŒÛgO”¨Ó;&·7u‘cã\4Úµ|rmZ_áèÔЄˆ•ž™ÜÕ‘aÕÊósB(©¥4ŒÖIC"Ì'V”¤4'Ö°lZÉeJFÊ”SDÓ #,YRE±˜C?¡h'2 ¸ÄV#\²A¢‚Ö¨o.Ø…M¨©4iX,4 £`Í&Ó4<‚€ÍhÓQ©«#0x‰(wÉa#/Hx²Kú\ì.öù¡¦°ÞF‹= ê˜(qù«#,Ó÷¨¡§njëÍUwÎ)#Ⱥb(Ò<K³<;G.’ ð›véšÌÝÝÞÊFÅ\徺Į0äN¥&ŸÍÊ‘uGž#–œ©˜fŸÌ-S(Çõ}ï]ìgÏ;‰†n¶K-ÕÍr³`°Sb».¬ÖfÏVoííì†#/˜ì-4ï ›ÍW)¼tåF¿3rƒjq9Ð0I;c­Ó2ÅÇ‘ÖpÅ=hì`•jî[vcxŠ|¯ù^¸Y)2'çURœ‚†Jl~åkx¤ØÆ(QKSoÃèw0Q²÷K¥O“ ±³íM€~3µÀotgRÉÅŃCØŽ2V €¾qÂå—jI&ËFs#/ê‹Þ&r_\xßQ(P^@'3 †ƒ:Î5©ïplfÍ„§ùvîLõ‡í_¶bøð.ÀiëRM¦EFR¦yI¬v×K©EJ¸”†¤c´GÒò—¦Ï³~KnœŒN9:cKk*RDÐî×1‰x÷â>kºÛܹ#,Cêë‚ž ââËaC?ÅtfÛ—&U¤ˆÅ+5PÅ1{;*ÞËš|ÎÌ8„>Ol°Ñ#0¨Dë‚sÓoê×N#/,!÷=H•<`ù_ T÷bŒ9ñÇ¿#,©°ÿÞÌ-chBáŽEï6gl:ñþÎwý¦Ü@€íŠ¬b?nfuŠ‚J– öiÞ°ß ŽaMZ@U:ɬ 7¶ ¿ß‡†p/'Òõ+ᯡ"Í B~Alm#,ÕUKr­t©"×6¨!"5(ˆ‘ U$Òät¤Æ2Kg|ˆE:šnŒAÃ1‰¾u'^Ó3kP q 'O¼.V ø팱{?U¨å`F¡Ñë#0Oaì=A<³€ ÷ %#,3[ј;â[Xh-cãμ9W€ð-º ß>ð…òTä_[×W­Î¯Û ròLWçjRØ ”8fNjã4 ÐhóöêüµÐjíßúnæéA›jù”B–‘Ry`*=•IÛºŽA/$6xÿ~ü¾-£>øÍÏé½ÜÀØÇÓ© àTö&,#0±V#0 >¼mðM+¼‰ØU½ã#ÿ·0¥ÇöU¢\Æv©ùŒ7T¹&Ë_RT®È¤eT­¬SÓP¡IÝòÛÖû!áb´Fþÿ˜6nµHæÇ°`’Dz ÝóÃOÇ´f#,Iñt=£VïÝ¡ÝuU$›6úú¹¸Û:ÇVÞngý;,Ÿ?n?4ÀÙæ³<65l<~½ÚöÓ³ï×鮽—‹ ×xtE„F8Äl=šYIîjUà30qrB;™$)âÝX·ÏÄy3Iâ‰m£Ó;lŽÏGÍ¡¹8¼ðñ¯$Y‹*˜S†XÙp·_ÿ4F ú½Ÿ¿+÷u 8Lo‡WPUCñÏR¯%â–Û£Š¶£a–6Ý°¶#ìý{?2#,šSGWþPÂø?–ÕŒjMŸ_ìüÙø€$çE P–ñìæ²ØUC#,ûG´¨ï¿Øš¶ôä)±àŸ™žÜI‰ü'–nEŠ€¨)"‹>'kAÍ©,Ÿr¹“²¥‰Eùœf&6*þBø;nÔQ¬·s®Ý5s^#/øq+(‹ùï6îeOÕü…À} ˆM%dRiG«¨,¦¢å¦ÛÚì Aþ$,L ³ë¾Ë4bÕãA*î»[©n[óלZ^ŠÃj=SnŸm}?f‡ÐýR°QeÑ äñþz”’‡ÑÓíÿƒ°¼ÎÚÂ9D£ŽF&Ì‚ŸzRCþƒ$’˜w½Àßä¥4I¢R4È‘õêæûÍ«ñÿ;«á‹IµìÛˆm½I­õª¯%µ^ïnîîÄ­~TÃöf¿ódûS²íª¡-¦KgŠY„ L˜Pìî<Ë~…5/ß0S²ô'ë$>¢e^—ü¬^mÝÿxË4¾m:—÷Oé‹÷©Jg=g÷´º˜`ìB+{,˜c„­vÿ™(ëªo©Qo`Öàè´ÁýÇ ²¹éGâq8žQG8ì$Øeëö¾Âkߧ¤ëùhn 2#0èô ›‚ªŽhp˜¢„Y0¾²}ék††#0ƒÑã§ʹ<_#,”ÊT(D†óÜšoXdQP‰¨–¼BĪ{*óA×¥ãüa›{+ÐÃÝñIŠ…‡@Qéµíaù[HnìéU„…úªM&*ÄIúR˜KB\æßÊeåBæ`¤¶\è=hï\wùþš7‹O»á>ê¿ÿÉßßú'Ø#/>/‹Þ#DyâÞŽ†`,6yópãgîTÇø[!™W¬=<+¾ÅÏåÊä$j?YÑïØ"h>s]bæ³:åÄ.ã}nú$¯ƾ®@0¤[’±›«ó‹€ä 9m™"ðë›Ì¹íûiUuF®+1ÈdóE³¿4Òç¿÷AGêçå°Ø–!c²³ðû…3YõD.Ö_ÃjèzÊןÅݱö6ª['~y‡PðÌ Õg#0³°àí½HÛVøy7R_%TÏÝS#åP•ðcLî <»oR÷¨cvœ·×u´[m4ÖßÊ@Z#/À€kÒ~K ¬šÂÖ]!¶ä@ò»oÖ“‹³9ÎäÌïg=“C˜t®ËÈà\£Nù2]†\]ÖhC®#0Bª§½^&2b@xÕ.H»i!,)óü*XœˆúHòœ†÷ì{ÊoG#0ßD²¡_ËfÄr|Ö|wþOÐdúÏRÆ©Íy'åÏÌp.wâ£ØcX¦Ëëûûì#/IÕ–1‘¨Ñ»ó›iØDœþ=‹œâÖæßÍBü¬ô|³L‡RÓÓ¤éuyä9:J’œ`Ì;”Z*©¢³0ÎBâN*W˜ýß“˜?¯}.æ#¢õ92 ù¾ºàT¥ŸHzN"@'QÏ%Wð·Ñ!_Ú®LL;BDÚ×3”`#1¢#È™x>õX‰}ÏÖø!PP`À]Ì#,Ô7çÅ™¦ÿð³éU‚b°™5øZ3M¡ ˜Hl}yšzüa>Rf¶/6xCì=çaÑ)Â6/݃\ œįB°Ë‡AË/…kóæÏócù°ñû;U}:PØ°§ÈâÙ¹jêžur·:ëOÏù¿Jn<xšzf7ñåÏTþ¥aü™ÛôK€C„þe+÷ñê©5¯¸XÂ’Ø2S#,¹BýéÖ,ºB#/âŽe}ÙVXâz_hÈ8˜|;™™u.yjÕØüuŸŸ¯¶^oÏóÌùšù¹`zz¨=ü˜ùªùµ—說pá_–ﺿ|-«Ï›0X.º þ#0[Uuß:›Ôȳòö¯£«òÙ§uÚnkc•Ùº>Øvd0Ù«â¦ý–ˆQåyaä¿v|d¢vÖöþÓŒ1ËYoòkëã/ë×|3WC­á‰Ãf.‡&Ç_ºMåVÊE.êÂ?vù|7ýüí—¨X>×ìÇŽYù·[ò¼é–÷ÍmáoGuy¬}\ÓÏÉâÊÚ¾LmÀwÝ‹Ç“›:O÷'§ùÏ×ß ¾Äz òHè??Èì`½zÏ͆|òžÃ]܃çznÃÝÔñÅMrâ9EÉåçrxꋶWÊ·óÏ«t›F/kÿ([ŒïruS7‘ÔmK[eáøèßGsýNšÝ»›·“ùʲ÷æ¦Ã4;çÃFœÑ¾·ÐgŽ§þìw°ìG:žA[ºåUEÚ®æjã› Ê.6n–e¸,í®­3²ÎiîP5õ¿¥…XW-Yet͹GÉ#0†Üó͈(¸Xç‰çØ!0‹•×…¼4d\çs+šg¡É›Õ^Y滼œ7]ª®ï7|Zöy­DÛ,5üOò?W§lûu]m'Íűݳ;CÄ¡‡1`}5ÎÞ= #/üifo-rçèÐÕ&|:@âôñIX‰Çz¡aŸý®¬Çg¿;<o!óÍÂ\¯×Ÿ‡´Zëã™]ø<kù‚gO/Ÿ•z¿?o—È=nŇ»–«©8„ÃyØ)¤e^¸W톰ªú+¿±úsx´][vfOÊë"®­|c^v+è³dºpGç\lÿ¬ÈWö€ÊÑ'L;¾y;Ï« £Ó#/3—"úÝê¢XÔ~Þú¯ñì³#0(äý”HŠèœ—¯Åñ¶}·ü_ëüóâ=WÇa¿äOçîÍ#U~]‹óãtèÜù6»%äl¾#,žïŠ6ü‡W­ÂcEöC?‚Ç·“ûµøãƒZrBà+JX^k¼_W®ÏÚåôÖÏí­îv-oLZžÎIÕ9^¯íæö3½wɯN ¯y^ö¤$¿ìèÝøe™+r2*wÞ/‡³;¥Ë²›)gÁéþ”«æÒ9xapøÏñ>ÿ“äúÛ÷¿?nmUÝË#,1v>Ÿ<÷ùs·áú€ò+õ·ƒTÞNnöþ«üñaúYD±óoP±cTwyÑX‡ûÇ×Û›ÀÞýáR).ÖE¹ÄYÅ\ê 0ˆ~¿ƒØÐ2cÄ#0ŒŠÑ+(Vb¦—l\ÛÙïÏ´ ¾Oפo§gP¿Ý§èõp»†cðùý¾>ŒzD¼ÃS3iûúU¸x§¢¨h;ëé~æJ“æltgÓòg·“é5º2‡Ö½h:ÈÝ0ý·rŽÞ¿Dú?<ûÅß7W zGâ7qáÇÅÚ?xP·ëÚ~q#,<xhÔ€(ý»Îô“úÇpö~俈ù¾¯Æ<”Ëã•î#/|ºÛežÙŒÂc?‰)#/Êç#0Ý^å[¦=Ÿ°ø¿†¬Éì–qAÙû<|YĤ?GÃ׿fà>÷üçÊ•§Ofö¼ž{Êô½€è?Y 톶ˆaL§ø±4øá]žÑí-¢ìW¸·úß9òáèÀé߆¾ïÓ³Ÿ°nkãÄrÿ MÒö+ö·bŽÎ¾EÏÊïÝ=õL™/¼öÎÊx¦Š=àŽÿÛ󾸊Ñâé…>Ã=þI‹­ù5t÷ôñHh~ÜÃK¯¨SÓ”ÈØY#,(ÇiPœZ„$Ý#0*GÔÁ„kÍ.S£z¥DÐMÕ8›0@(ó—xsTh¬@2 åIÈè+«Xõ×fƒË†í¥Q°|aÃ×iƒ²Ë5|×վἠê#­J‘ÐÁ€ÐúµÆñ©þ­òK+¢cÂŽ5m]®¼‹D½3$°upÃ`†€2øÓ_ß»>¿óÿ‡¤&ᕯôëûÅÞ¾¼ð€$Ìv9QÀ©Û +äÔPÁTÊ}@¯{áJ¶ÉÎiS%aMŒÑÞ9!»®3Â%õc›'Ï^øãWÁ¶²óÎ7vL­«Fðæ’0®ª0✼Ž{g~":Ç/ÌõXëGHc€Ã^œØýGÖSÕ<“u9B£¶õ®#/Z‰#0ßd´´ÁŠˆ „“¦ºý¡i^°IúýòØ’¯<5G5é<)f!´æAŽm"í¢«#/”G ï—sÀŠ¸fò_WcÆh£ÇÆG—ÇeÖÆ¿h’CÅPBJ“(ѯd@pí:N2Ì"ö\Á ™|Ñ«Ÿož i÷åð¶ñg¿˜ÆB½~g÷¿=°ž>úº;æñÔ:½uÔpçOVgŽíYë„õ7etªu' )ø3Ùóqä#‹Ôéh¨óã2]?[¯˜ÃQÓ£@_§_.Š» m¦ªg]Õ~Ç/Çö4©zÝÃnjÆõÓ:DòäL™ŸØXÙmfÙ‡KâÙýŸ.ðã캈俷Ê57&à/cÜ4ÇÍ©µ0ð<æý6.w<¯åÃÐÏ÷ßñâ5ÛÆí¾{¼±!¶)’M5:³>ŽgvA°u㺪ÝdÆ6„ßÉø:•z/º]¿®yŒ|ã#,Ý (Ô(6I]ÍBI.áp»Iü|óWóý•¥\™†Dwy·_ˆuBd”òñËäófÉþå䆻[!¬¾Z€üñw~›>oòù8€3'ÌŸÁÌT¨°|®ÄkZ¹‘µ[r±ž˜©cˆ#Õ"ü^5²î£ÚÝkki±aA„Ô¢¥nØ€Øá‘a ª­(8Ü#0K!tÙ®—qd0+5wrš³,ÍTthX&°‰WdÇ`a¬sZM†™&ŒBÈš¡#0Hìh4ÔF2¸ê‘è¯NhÓ}dµAa¼C"’’oû´·½-3ƒIp3D$môE¡=X­2m:ÐþïÝ{­èï¼ZÎP  ùþß1ø,?a)ŽÊ1K‰÷ÿÌZÔrZ‹×©ÊHXvXEÚLÃóüyé/áý[ô|œƒ;;>ýÈ4¤mŒ±Hèé@ŒÖaëд›ÔÈ6,*Q¢¦“P„5žÌAšDP•W`¥‘•Gõ*(ZLÓõeôàžél·ãËß}’ïêÍo@—ÝwLt›mdoéÉ}-Ê)p°fŸ1®¼ùád§5+Ç~ª˜oï¦ymÍ–_¯_“ÑâU«ìú»ñƒÇ(ýoß»OÒ:^šÆ›ÓŸòÐ-¾#>N“YJ¾[ bçyúÖUWQÌo®—y£„{³iý¡[¾ áYFO•(H’A¿{¾RënJÇØ;¹|ß»ð_|¼¹ôxs”K‚)_œnÞà†` R­Ÿ^æËS®îÛËÑ”9¯]Ç5’$?­nþNG9Ì÷+¦æ°0´0îæëºǧº=ñû<®yÐ"ëþQlG‹Úí¤s¼þ¾Û;Ý>oO?ò³›³–]„Ùö"tÝ_é?·?žbÉ,€˜ò£~’ƒ˜\†Ðƒˆò{UE1?O`(BÏ*Å‘lY>¼§š3KÄÅnB“jÑ"¶”ÁÊØ¥;d§•‚£Z!¡”p¢³ݶ4«M«»µÊT›"’»wÕæ½5î£#0’²JÎl¸åª•#0¡QB¥"•Š"ŽÂ× мµc0¬¶võThyÉÊÛÖ¡œÃ@Ù[dáŒ+QŽ¦)q¶"2An$Þnçƒ0¤c]¹ DÜŠ#0˜)F 70e[1M$`áÛWUÓd¦±Œtˆlƒ̉†f*‹H|AwRÚæpI C†ùœ2P6´&T(Æ£#0ï´K4øofµhlbbÉeC…N"G«zŠÝ&“ í4¥­,EdÈ«Š1Et¢¨Œ*íL¨Ç:ë%#ÆÉä#'J¸f̸ø!U†$†¤S¼$=?D80W’Í'D½êÔA|šà,Þ%„"øûUOÖ†ÿi×ŪÇÚËIŒKˆø°±ÍIöâï—áN³Ô:(PžLõóaèú¦žßÎ×åŸÉßv ‘ 1™ÞxÿeÂÜè¬>7øÚú}£‡›O.§Ÿ ι¢ž!’A5¯K&ã ¢•",#/擹 „ðöxÓf€óHð ÏbüÅô–l£#?}\þ5ihŸ—Ÿõš?¸üç ¸ ¬°Ðç.gáú>y>@ÿ{T!ÓÄUÈå€^b9 Fgí)ØAvc¾™ ~m\“R%Â#/Öj¨9äoM(3/ÁØ\Ë™AU$’̪ªÖu(úà‘Ö/ÉZFºÑãË·°D8G¼ï8!úÍ9o}Îü~5à%! ü/ô á8nQ"ÜÖMÛ‡“Z"bñm¶Æˆ ?Ì‚ð§'²Û É7‰.ÿ^{¿[•ñö5¾Û¨.‡FLèÇó9Þ2hnFDގ΄fm—‹ý7%°Æz7^›+wøM$—S?™¾á÷i÷oÞûª^ˆ˜wV<C´‰¯?<¬÷{”.bJ)F @(ø s/õ³cMµHØÉâ2DX†¢_ÌÅlä˦ómðèìQ c±J¬Q5UøGÑý>=¾.zùÇ?ÕØÓ#0·â%OOºÐRɆÒ0H±#ê5͇§$¡\èš ƒº+P®ãA1«†1‚I‘¡¥aÁ­ÚkE¢šÁp3µ5º 0Ë Â!<Ô¬Ó­uzè« Á­A‘¢ãŒÁàâfhPi12ŠÔX_¡èâÒñ/^F&!êš™RéÚh>±S&ß!°Y¿ËAeŠM°¬jÐÝú͆¬o*Ì©H—y—ƒdEX2&u|3%–è4ÐøÕE{z´ëkU·Ûp²&JjtÔ:¸lj@lQ ÙXº,#/ŒÓM£aÃ(ÔíJ¶Ë´ÎÌDé#/Ðá1¶'[C®Ù`Ø2麰!“® ƒ²QršsgßøÿN††²<0c#/m%åj…FõÞË…×t/ÀëÞ‹½mÕä“Û ðµmøÝï#,ÝL¸ða&#,°‹"1 Wðy¤=@©J#,B#0‚,è·g/Ž#/=_Ÿì²ü¿!ým‹áB§Ž³‚áX2#S ‹é”ÉÎö¾Û÷GIM0­Päƒ xCžÿóórM8#húwÓ‰ÆÕ v©DópÚ~f òúIó‘ó~“³FZ¹uùñ Ð è°;ŒÏPðôqqtDe–AbŒ1aiSýpÅQœiwxc۬Ϟ¡¢†äxEÍÆ—#/"$JN×R#ƒN@‘‘"Fêa#/fý–5[ÒDæ*#/3CXcý}a£Dž›¼ÎÖ1m@ØzuÀ¡Þloo‡4ìy´Rà`#Á EºZ Â# u6U"Ûw079µtª­BÊœ¶óþ?¿'læ!8Ÿ:¦”ŠM»ÍÂhŽÍ¶#,­o§ÏüãöyümÓW·ŸõßÆR¾{êw7>a,øöä÷à;|ŸË¾uuÔÀ†ÚŠ×Ýü Û‡ÞÃCÅ—Ž$ÐH¹i@¥-M.R67eÎc¹Âes Ù§G\ÆŠ]vã÷{¯™ºr¹•ë¾»¹Za Aæ Œi)#ouQ•NЈ긅4›Ž"FÜÁ‘´~ƒi‘TR5ÚqÇ#N/J·^Ê,‡#0:jÍÌïdÉŠbÊ™ÃÅ»Qå#0–•&`Y)”Hà#/i†ûÒ±`#0¶¨‘”©E4Ôê¨iRŠüZFUÌ­ñ‚¬2ˆêUÑçÑ#bïI‘cdãnLr::H8pÁ(Ì#,Ôœ„Mïs‹ñCªÔ…B·¾ ™3FZe±‹Àå€Ë“5H”§!àbƒhÒÞâ+lV¡J£È¤Ë$cD,¡X"«Ù{Ýö4‹u'·–U¦Q¢qš¨ÇÈÃýžþlF~— êzTÆÓýb'j¼æ&Ý7Èx[e…±xEi—̓¡¥ã³ÃN/UA2‚bõôQ#,šŽ¬­gê‚Qµ G#,Óê,ªÄÊ<|`æÎäñhv-§Ô>öÄ¡%üéÖƃo©«oMFä™L[Ù‚“¯-JÐä<UXN—QpÖ(,žÍêEëwqs4ë¯]? 6Ƹ#/Nük–j6äoj•sKqŒz±µZ$ÂÕ¸d€Ç­Vß=Gè–'¾:—¬ÌÙ¯rðÛ¾‹Ãç^,‡]à‰ v–8Ò“rÉFa¶o1H,X §¾° ïš¨ÎTglD?ïP†ü‹àØÿµš°¥Á±TãÚbÍ×a ùFcZ8ªƒ.!›XÒmÄam®M‡cÊaHá5¨0X‰Ìqغ·w!‡RÙpØ0V âþD¬?{8ן.6ì’yÙäuÕ˱gm™ÃY†-ÏWØ×W¾ãÎ,pjmG”í™ôG4ÈPÜì(2(ääÐi 0a2BC-»âÊÓ¬lÑ}#9ÛW»h 5®úðÔº6•ìmÃì1z•[È<­Uw“* îU—CA~¿*ì;®¹#/Öæñ¹e¿%¶ F ÁæÐÀÛÄ2£iJ-âß8¶MËk]+•øIh5¸Øn…å†æ{Žüí;Ð븣m¨ê:ìž"²o1,•½>çœÞ[;¦ß#/+¥Ô`*Úd1!¦3+X©qÑ~ˈ¬Øí §±ŠnšºÆ#sl)”4±\³X™ƒÄ5K§M˜œã1dášm–!‹Np^r*0ᾎI¾ÑKŒMd®—vÀ°­C4rç9$Iµa9M—™¡ËÃì÷«Æå_nŒVE¿M¾ÉÈÛ •4!ù‹u³Qçò¨ê\àîéöµÞž–š‡>w±4Bˆˆ¨r<DQ0þ3öw¶Ç»#-/Ž¬ì'Ò½fñ‡/1q˜Ñuib•5[+å³êÔdI̼9Ñ$@V#B~†TÞºc3OÀ¦eâÝÿ’Bâj÷m±”ý&qUþ(|Ü®_³¨‹YÄFàªÞn~#0 ·™¹ìY\,†" |6UÛÞ_9µ‘#|íRøÑ8»HR¸³‰BËç9>[<D¹¡;$¤W¦§(­•$x¸7Ì& Q€©ÛÏL™4?¯ž‡7£JéÇo àÞ0c#/{Ç9)—S¯úûD;®ñ_lQÞøS‹EÁA*XêÝ¢ðêؾ†ýWYɧÈBzó!èDêYΘ¬ÛêD?W5_Ôãí¹ 3¼Í)KKÀrtʼnFHÃïµíŒ„Cų8FŽ· “}`Òeºèá4ï§廆%Þßkt© ݽË’'¢tg#/]éõt¯!I&ãò§ml%Ù\aߣŸ¢èˆ°åýªSi ƒà­†Aç$ÊŒ\2™LÍÔÓ§žŒ÷Øœ¦ûÓtÚ¨)#0IS³Á‡#/¦Žn£",UƒiÂÕÇB‰œ+#0:r´„Û=XMν0bˆJDÂc»Jp„Ô‘M¶µ†«`”Æ6uÞÄÿå1Gqøì:Cšoïß–;Gefû;‰#‰¤%VðîïÚCå:¾5ñß’ñœÈÔ$=åc¢Ð¢7zCö±»Êa(œdQ*ÇkŸÄ·ÔgÕWêÀw|ì,Â:œH9dàÁt'o”;ç—Üið  "¥O¼ØmäòzšJì-üIVK‡‡Ücu#j…ŠòÇgn@›%<ž'>n×;Z‰ÈdòýPæ„ îóÖ€'{¢¡…@>ÏRº?£´´®QÛ‹ŽŸè®uÉ°>3âØíù™Ÿ­¾º#/;´¿ÀÆôªºèÍÀƒH¹9œ2Ùh@H~¿Ã…Æn~Pð{ uð g)¤Yàf4‰b¡å9ùx÷°”#Ùï^ñ</íR€`BŒ1,Ä* [K#,à‚}Óºë{éŸMW%·…#04† Ý­ÁðƒÛž ~~–}sì¯;â>ûÑú5d]YÒ}¿ ˆÃ4.¼ ahäÆ#úŠñ«¤¥Mr xó/<DF ¸s0¿Ä´Ô…2~pèÖN¼r‰½ê–#,r8#vèc¥Çå$Gh¹Æ§¾à©kvx7`M1sÖ.%ï–ÙÉ€ý»}.HÒu•s—]ŸèÖ8™8ýEïjïýæè I¼ön¡G^&LÅSå1ºùåÃyg²)IVì–\—¹#0Q´Á*Ãøiv@uá4p‰˜E^tÀ4x#"Û„œV{î>Vv<ŸK>»¼<䘉~ö€žÛVrs< ôŸÈ–+k¬xÍC»UôšB$*dQ+ë æØØSÛ­D÷:n3^Á÷‰ªž[k°/´ƒÈXnèë»ì£%üguI㵊Knî-7§÷ðTzÜ·c¼Ÿ%ž…Õ¼ôÛ–r|Â,‘ðã¤è|F—u}u ÛDÜ Ê$â½á2I©¼ðÍk.ËŽˆ¢MÄö?»£ï:õ–JIéªôAfÅÅ„tCC¸:f‡2cÓÄôã[ëµ›”Ð72^= 7رî¿Am>èë/àOOЉ´êiåÁš,ùÌc†ÈJõZk$ÿ^zePøˆ8£§Z#0u»6nÝ}È¥ñ—I2Ó™†¶>³4¾<†3^ñÇG»!2Ä‚n¢ú–ÕGW'>úÅ®¹‘ÿ§¯À?Ü›ÑCîÉ2ènCà‚Ç ü¢q´ð ü¹Ÿ|2Ó¬7,šã&Î;fÔ¥5¡œ’ïÁ4Ü ¼õ‡>x¤´ZaÐéÃôÔÖƒÁÄqãŸ~[Ý·^š:+Њ!‰mÞ¹¾#/aŒ¶Œ¥3–Ã}-¾…åˆÌµ´.Èòš¢žPä=Ž¿Í¹eòîõ3Æb"€ÌÖ.ÖÏ/Còw݆±ATz, Zk -QûÌü~^X9>mïÞ|ôBá..‰ÄªÄB…×µ|{/]Šzs”ìc”’OÛ—Ãy‚ݧ²èÐæ#/ödñ#/1¹Óå¹e—ñLF~Ø<W ÁƒÓ¾ô68HfÁ1GŠQ t!nL9(!TÈߌ#0®GÓ«qvNN_¡èíá5ðî]¼2ª]/ÏÐMŸ:=™¨üJÿ€ßo^ã×Tº¤¯ÌšÊ_Õ5³^çF]_»Ž:Ø/×#/özF‘‘Væ¿TëZ ¿ü:ôÚj/òô}W•C«Ç§]´kMκ‰¡ÝºÃ“²A¦ DV²4×ïç9ÞæCáüúhÏ”aŽ2]¬®½¬ÚùJ·…пEj$w¾Ù›z÷xâàúƦ°Ô’z¸ªÅ(#ÞŸÒ¥Yåû\/#/_oAsvN1š Iñ¥ž/­¥¾¸É4>XB‰xV®ÞÕ÷º1žnæý3§N†órf©'~ðóvóeMeZ~4[BnªÓWѧú¶ç÷MNîQáqcºL,ŠÄÒ°²2º¥_r ƒ•$³f"EM¬¸Q›}QÇ­cè÷­¼ã¦œãwÆa4‰µÒ‹Óï%ôÖ¼ÞÔm˜É××®%Ï7.ÝüH«´€¨f@øv-ölgMÃÜš<à‰ïæCWðRñ¿OEµÝrŽ¼yÄIÔãaµGˆEŠD•g&wj0q+J^ÉTÈUÌ©)JãtÃF%­@l¹Ur„Š™†vß·Qé6öh÷¦ÖÝ0lyëA(·77žË©ŠðлOéÝÌk>~øô«\‰ÎùòSHq«Ä?›Î®:¿\x²öweÉ#/¼ºjÆZó‰ŒîQë1ïž6«Ýæ)CKfýN÷ÙJ-Ñ)HÚ¨s'9€U#0ÂË_]B5C70lÛl“ìe¥÷ãl.a°S˜ÁL¡ÞW1!Å>È* vQ.C´Ôh¾±v’ç¤#/ˆ?˜zQj…çÌÙ[ÆXÞàÍ‚ˆ©&“Ú¯¬ fÕ9B·BóÍÕd|Ý<í¯FþyÏm°Û Y6Ï#,vljR<ð!\;ó¼Â ï/0ýÙ9<¬±16Y(.ÕéÎ!´ª vI“S+R€Ø‘€Æ¿‡).w÷VèoZŒ}ŽuSg¢‘ÍN'âRøt¶6¡/µͱ’Ñf6ÛfÆÛÔÛ{]í#œK±Ù{mæ˨:MÌ»ºººW¹=ÏRöý†^ˆþŸ´ï}»‰u#¯;LγהµÚaõ™–ˆwD" Õy,ØOãËÏR½±©ëNK'I<"ëΚC–¶OØ<UF)¾•Qá·íÒrZFù"TA8EÛ©fFVÛ›[ãhròÄ(ojuýì³"46º9ûTDñ®"[vkA¥Ýðˆ˜žT2ìØûòXþBö û›G{¯Ý!t–§Ëž#/{XÆk($ÍÉÝ4§AS“˳¡¢îgòùølñšÖtÍ)Ej:xvhï@õÓƒ|¿dØÎ,Ãàvã>\‡‘p-ã6Uã:¼56%vÚg‘\\ ‘[ ÏG¤»K¿éÔ‘Œ!_O ˆ{•H®Š<Í@hªŠ ßøôQÑj*ªGksÇGæ5`÷›%a¯#0§OtáK‹ÕmzØBƒqXº‚R€â£<—Ÿ*;ËŒÎÊ®uƒR“²#/‹¥¾·hª1ÛdamªìñËî¢ Ö]Yv,ªj_À38V"µ×¶b©B6Š^ ž¯bËD²ƒÇ€„1+#S›—…mb—µÎ#/áôiª·ÉX^\%Üõéκæœ'þ¥Iª¬úƒ²ªkðͨ]ãŒ3×Z@æÂÇ`¥ëµØIœ)(©5ç‰vüØÅÃÌÊ3ëÝ\Æ&qÅøRÌ9ÚgÇ^±72«rn’âçápV8ûþzòçoïï'ƒsÜ;¾_Ž>¼ùÜÌöñ¯=0ÁÄÉ9›_uÒ¾3ÌùÙ#0Ÿ$j×U˜î[îqx±u:ª†º’ÐU†HëÄCË1¯O3íŠïÝp¢]¾±Âå86ÄÛ[ËW¤«Þc:T‡r¢Šì“;¼g²IH21!¼†®#/k³áœfvO«Êé¾,ÛYæ%Î-‰q)]L4Ãî—Œb/Û·Îgá4Œ꟎³ß;sŸ© ûè’™Iñw9ëåJ§ †•Q³ï§¼DHhãTêE+eY–Yí+€rÏý!ö*WGê¤÷ƒÉ õ4ÅF–¬ì5‘–ÖÂê¢*uóØ$ :ÐÙ>O‚´<¸3¹ßMýúã…S“… ŒÎ d­Õmâ"Œ"[«¬¾-’Á–Aœ*Xªé‹Ü.ÒÍš¡ùmsæôTñ¨ðj?¨÷¹!uƒiÊÆÇ6vÝR—¾8³u›¤ÅoqŸX÷f¡þÔºëâ®.ûð•Oº%1"]Ükœp³h R팄þ«KXL×"Šºn iø<”RzóôxݘmòýY<Ô¥Õê B7³Aµß›%…_é>«3‹NŽÏ<y½üj-ã½ï"Y¯žÏ4.3úê¼½üÙÍ?)k™—…à Ž Š›ÁÕu'ÓyÎÎÙû裙ÌË‚dŒáÉ[ù>¸éé×{,çO»ª^ð-š)AP`Áý’£²6”n¢ƒ¼€í†‘rénÒët·Ä1šÎç=ényÃN°'G ŒhVø”Â\bm0ÆGuÍç™ÊÝ·r®½èÔü æÿ*"}¾»UFü<JÂd^˶Ö{„¸XÁÀUÁL\ÂR5Ì2É°q2£>¼'É×4ll \íÒèYG#©ÑñàÀV¹‹#0Ý“š£»×ËÎóÎjÉ–}'UðÛXí¼É+í´®É.{£GA«±Î"²÷#0e š¦Ñ›\r†=2yto•93šqÄÔâ¬y(r`ü|·¿èŒi>Ñè¶^®u\úæö³<;Nzûo<ƾCÃôí±{9ÄÌ5³¼Ò‡ÔjÆ.~‚’!F÷¯…UÅö¼)4×ÅÏ¡‹7k²y7yŠ„;mšhÍš±P”Ä–:4GV³J¥]·]Ó¢9[ ƒ„‰8Φp“ LÚKrº«¼7¾TiJŒ#0,ƒ °$@#/KÜt@ ã3¨;ÈëÍ]Ô|/F£bŒNÓe¹é ¿d#Zµz7n–5‹Ã#0UHVñdZ*¸({?ÌMÕö k”¬B’:HßP²¾º[:ip>R%¢Õ0éÛJ刾U0¾µ¬ïµi.p5Žë»}ôu‚À3ÖÎYâ$•ÜèNŽ1Ùv-°f›–ø٦ʳ8cû®Åóè‘økTêñ‡Š÷/–÷º”b_ÈP6FoŒ3³2b϶=4ܸA‘‘oZXJR"H‘‚„ ß‘euyDte¼º• #0E„‘¼¿-°»T£áÛi/aöVðEìD^{ûçž0qò¯[®gÓ¥êï•,ljAÞžFzƒ8»SRnå‹][#/UODàî7†vŠ 7©Ô­¸Yã¦Sj]-Ö¼­xs²#/ úð\lLN¸B‹³®øadžªõÐ:¶ ‡So2Ì6MP+žçs#//p»âá8ZÑÆÏ^x&~Úƒ!?¡JÆ!C¦+jØ jÒ4ÆY¢Ë,(á²`;ZàcERs fàYðƆ LBôb¢ çªøκïg8ÚG»AÏêÂ×/G¤µ‹ý+š~*&\\—*›DU¦uÆl' üáóð›Ò®Kl:(NÙsö½RGÞ.ÅÛ¬òsíëx[æŽÿ–­!$)=yóÞ¦¸1·êÚ±ð½þ2@Ña—qÝ«Þù–w¹RîÕ³¶®¹Õ/Úô° ÑX:N¡²ƒ~¤77/Îíéœao¶õX›>4ê,CêÒ¶X½|’õb9áuk}Û\çWodAMÊœ¬È™î¨Äû½c‰{Æe8SµLg˜"du]\pZ»Ä—\j‹õêOÕ‹7ï…¼ÛÞÚÑ>þÆw·ìÒ¸¦‚MRË$ˆ¾#,XõªS4µµ+|cJöŒ %!]#‡{#0UâW4êv•ZÔJ`IpÄÜõ|åuJ™¢D,*,Cq<w¼pœ'CëIŽ]‹úñi½Ës "R«xw]”Xz–ð§¾)tóó³?›Ï™ë¼œíÛTˆæT°f·ƒÃ©ÍHòÛ#/*¶!3ƒŽ‰…ƒ…ÑTŸŠ¶tâ¹é{¹bë2àã$.DÊ÷#/ ©FŠ­p<ZÍ\#¢Œe#ÐÉçtÊP^¾Úyn¨ö[ÛlcB^Ïï7ìÕ&vÔ4¥¨Ù¶¿ÝXêÒ9m"Ôi¥a5ˆ«;¶<-eÏ…'ŒµµU¬$ÐÝÇm[Â*L¥J9-‚ò?ÓÒÓ0Ãõ¯ÚDD¤1íWž¨ktÊ(úW—²PŽ–椼L—iÁѬ®Ã#0[Z®˜ôGyåF+?EŠ=«Ï»,9~µ'_lºÃ¡×nñÑŒìaû˜ù²‹š¥E¼TÖ#0Šv”¸ð7J®W`@p(€@:œþ~¾®HI7öIFÇï½ÍZy‹™¶-\í«™ÎåŸl±·x9«Í6‹ê-¦?A!Ù| Ø=3Í!œ#,ªa%#/‚ÙvD[RªÅð ¤õ•®¤[uìo¯YÏþï=¼—– (!ß*jpM…(Ùibù+NSá%ÏÃ?˜Õ!vð*ŸËàK¯½ÑõÏõk«tæÓeMÔù`ð¡i %ZŒòææ¶ò•XY1Ϫ •»nû˜#/Ç- Øõ«Âœkpª“Mà—Ì+¯©r½½ò×RÌÒg˜=ónfbÒ$˺ÁD¶Éì°Ébh/þßn§|ÏVs¿›¹ðC­z§jñÒ{ób€ëÞ*'{3¾{MîåÄÄÑUIsdÃúáÏlôͱö!ù¨é7ÐY«dg1Ù6q’í*âÖTsYEr½ë[ê “r¼£@×îVœ˜5a}[¼váê„Å<½T`0:\ W/]²x(^§GhìôûÏõ×Ê nyò‹Ïj= ÚO‡wUÓ=n;Ë”{ëåâè½jy›/žÈ¿ÇöÓš_#/øç¶å®»íu][EyKpWjïŸÀ^ó{ûÊÎqQ2:='ºœþ~y:(Ùî:ÌKª ³’–p\`„¿b PÁ@UU!O6Ñ£gê*Óu'@ S‚³hô;ûÎyA"P¡=[ÎÓ¸r<ññt”EJûœä‘ì`½0â+ ¿EŽ•”¾Hžõ‚VP#,þE¯55ieBU¢sñçÏÕ#0¹Ý £¤ZÕP’Ú×–^&ÒË“®îtuˆ«úZ£=pí°=¬.èuNñYw0=QìŽaZ9…*„À¡¤šêŸ6m1¥êc\EAãÚ”Ëâß#0Èu¡P*ÚqÈ;\/›¦‰.2ÙtF[¨>ž¯ oj’‚àÈ*(,.r'·´£ ‹j…žCi%qòJx‚ê#§_8²¯¿äñ—wÈ2«ñøÖ‘¨I|Š.÷Ëź|”|¹ïÚ\¸iöøTÅ5päÏ]_Eßs*üë,²£p¢Äæjì! ‡º ©ðæ„òYȧ1ÌÅÍiå]q7¦_8Þ¤ÙM9G‡bö‘´X‡##ÅÊêÄÔÍ.®¨øYpžlV„FÂ0ÁÏ.#,‚¥“NáDö ²tw¥uÛQ3¯æ¡ÿ ½x¸;N;ÏÇ}Cd—û6øãËpüpM›ø©ñœÎHËÚ0Í(»œ˜Áµ7MñS©÷*;YîƒÎ1üºÒ;;¿h±,JaÝG°16»h¢¤b/b­Xªrl›C è%ž›ø^'¦Î¥Ž´´*;N¹õÁ垯õƒªšg=bxØ¢—¨Ý#8ž*ž3}ž®„±Í`5Ñ6Ä&]»*Þƒû#/Á°k:‘ϱ߾ïò$³®®náà#0±Ú øúM׿¶b¶ˆ.Y» _<3[V«`4˜CËvBf7ÂUîзÂ^OYæx¦.tÖjz¾Ú}sm+„êq£U΢§/’5OV°ìð‚StTŠŸ×‹o7RDþpøû|pÄ<箺‚&Þ\ör ׈\ZŸ¿O'j²"-‹[o†ÂkõôüŽ5{8çÅGœÒá²ctZ†™Ù±Ã×Ö»«©ã0±LŠ…"ˆy›t ){k3Ÿk ®†s¹Fäf|'7š””·Œóó »Ä:pt:8â}žøò71ξVâ;äx·:(åýQBJR~¯ð¶ž-zMªÙˆòò5te‰àÑ~Êô­ûÃ2óòöÒïÓl²:îÐç-Ô\Œø¤‹a¶C4æБÛp8sLîOe…i¿_koQXçL§ØÌ&‘¡œ®î¨âC¨ð¦”´ÅuéŒÝ|jw$éösK+#dæºwÜ“V¾ìcUãúãËv: ·;Ä._îqÏS¡¨E§p§†•ÞÚØ)iáxôc5^D7ªlKg8§ò «rU»‚™“RÍ'¢ãÃðÞ°Ü@°xWºNLšN¾­ÖXA|yÏ¿†o;Þ~ï#¬ Zg ¤c·IpôjϼðøWŸ#05ÆŠYmA`;mvÊ%×êæVùÑ{^ Ä£ ÛüµÙVžf@ögëÍZ õÒº¹ˆ(:M÷Z)—0VÏ‹¬#?·¹òؽgV€ðA4‚¾ªçcôMï€ ;¡W™TíT戊á­îÕä¶#Fd÷ÅÐîôpÍ·#,M¬¬)pn’ä!ð¨ôí÷Úƒ×é]ËS<kµaê©l[zŒyÖÞˆ1Î¥ÞÍ)ÞÏN¢˜tâ'T#0A|6$p¦VëÞsGïþ5±ô*8sÅi@ÔžÈfqšõSú'ìvÄÌÍùû=þø3ùEùÓ<ŒËv½“’~­Ìû‰-å#0æÅ’°¢²7±(ëÜ2›¯ #0*!|-l·.)V-*$štuaÑ‹õBšžÈ„-»K #0<¬ÕcZ€FNrÄ,½>få_ Ó‰ÑÏrùöÁº#,¸®?–ê¿ïwÄú¸tþSá)g^®<#0#,)òž’?DürùÁïèF|ji³€ûå(I«£Ã´” #0=!ð~Nî.›‰2ÏH^i‚‰úú}ÑŽeƒåçxÛ+auߊSãþò“XD…TBHMheŠ‰Hµ&Ù6(Ûh( Ìõæ,F$K@BˆNtäyüÞ4q‰µÅ-¿ ¹\ËžÁ>øæÃàLvr7+ú,mšŠÂ–’¯¥U¯Ú–³Puöêhü«#¤ÂtÉû~Ÿ#åNÕ|[=Z´~ôófGö¦6£ºJܘNÿ±²þ-ò†(´cçìòíî ‰‹'Ãïïci"_E£vL„Ë:Ú)TmWe_ŸÙ?”U0U)#0%2Ín?©ñþ­M§Ž-M~š·íR+c»‡éÍíðÃôó×O›ºéZ™­£Âí÷ŒÃꃲ±mtÈü\cƒ,r1#0#0‹ÃjèÚ9pÔu]6T>0mÜŒåà5ŸÕ…Ÿ‡ƒ'Ùò»S¡ZžuÜL3d5Âz#,jIvs%5.¹N¨88¥ª #0×Á„aðÁ\‘ùƒÌŽâ;Ç¡éBtˆ‚r6¡ ÷rŸåôã“‚#/³>ùç¬ÐþêˆbƒõK¤šõp²ßãpÁGÏé|S#0XSæŸ%£x‚Xv-êUY–ñDMC®Ûó/קàûÉW3Áq*ЃtÖ•‘¦U2;£c•#,?Cc”y6EÇŽyl üa­@pzo5‹.ÔG2ÖT›a~Ÿô;Äè‘éuÒ–!ÅujgÇ&ûu'R˜D J8QME‘L½Ôîã(‡ß=ÑÈ^ÖÞû#¤w|ÿ®Û#PPé3¡èˆ©èÖ„ùè`ie+YĽY*3ÜȦX ã¥aÓ5§Å”¹ÔÅÉ2$ 䚔 „ûΑ(º´£¸Ž–Ñ,¸ºù¨G×Ƭoé糿i‹m®w¸ŸÏ#,¨vD8Ïü¸XÓ¡ëÀhOSM~g勞_šÃß|Á²qñ›a#ê“o?5 áâ >öšk¸†í´31„Lk‰{ç![K–{aá¥ÀøÏ>°îØÍ~ŽI·éÓ»b9ñìÚf¤QYUÔoj¢Ê R/4 ~îÏ0lÈ I5*ø×\ÊÌi8†–å[øröŸQ1¼ïùï ݦ2K§H 9gÒÜJØcßVtp>-ÂM2Ðø_ŠýYÚÐ/–ïÕGg,÷˜€í\œzéÆñ5{ÕÏy œQ‹/EÌ@\ÛÜžK€ˆo)ÜCœtÿ%в…㶥fê‰ì _tOQR”#0ŒØŠ›énjïfQF™Ïˆevž|#åYÙ #,}S#,`Òã·º,IñƒŠ´u[2q8ƒ;ñöÌ¡¬Ö åîM ò˜´ 6µ©ò”CHàj‚Ql|AKhiƒæ%µÎ 5¶›ÊAh! ! NQøŠ¤ëKã¡x¹wœì(#0 „:Wk¾¼âpŽŠ7\_›f­M˜¢¼¦¢øp±Ä3 r‰¡,F³¢ [ÿ-º ýÿ›×cËb:í”Vxs;ò€^#”“·nè³Þîj80–ç%ÏW¿ ‚E±çôÐa3ÇòI)¹ã<`!h,vižH(PB Çbîœf)Î×àЀŠ©v»_A.qw.~¥¸°i6”Ó4Çé|´±8^ï;á8ðÁ'Á…|æ˜ñ1è³Fqv`‹QˆÄ$%d<… ©TœÊPZd~Q„ |€yì³U¼ÙÒ™ùUeÏWµ½ÇŒ§A]„!5<øk¬ƒ¾¨K!¶9bvÝ_ªÁG Q3V‚Û›TÀa€("2#0ë z¡P¯ƒÆ#/»2Äð€8"ùLŠirÕ žÍÑ ¸Z«\jÌ_2+Ú·‹³xňÀ“‡.€ð”ß6â®Vã–aeaÇÆ;eƒ€¢"d¡HÐŽ{€›É¶ š8ºKd„«S•3È]yXºmþnžñ›#,Fq“œÃÁ”5Öìp¸Ï@Î0›ÒAÙdùe|dsHjðû7ᾯ6Cuféá^Âî¶BÐÐ F¢C’ló-ùŠ¬M&X¤Óì”@¥m0žPÔ+ncXÃt‡Ôîa kSŸew| Ù"i’ ÌxJ2$FßE:G–\Ý1 ˆ©ê™›ö™owÑs,#Ë–ôœDt%§žD!Ì·ŸÇ~˜/+d§¦ú^Êö›Fá¾cѺ†#ßÀÜ “#/¶# Å€*,yy¡#, âBE”wåkƒƒòéµËª#ªíÊ5^Èô'r£Uö.ÆXŽDÚæc3ø£}Œöv×¾r/¿uÆs&u€•3²@@à‚#,£¤Úñ+²T‰‚Ój­wæ~2Ã<ó*zp×#/9ÚÖØ¿jýöQÃ*——!û³¹óü×¥û;¦,ô¯ÃËr.-­–?­2ïqP‰#/ÕÉ— Zlœ¢è ûù" x°ÛU‰b„T€C@MÀ(MOmø2¶s’ˆ3Þsº³ì®m·ìð~ Øõädœàéöðâ G%ÄZ4“j®A­²Øò‹˜Ct`ÎUë(3ç¦í¸¾iòÑe‰¸¤¦ÑÒþdWw,/òmì¥{9€Nãô‡5ÇŽ/m\¿ÒžÎÆDÅû® ¦#0—uù\ÈÚ«H6D&&¤”#!C;Ó§L@ÕÂeç5dž:}æÌuÕ gÓȸ?TªÆ¥@êÅ't¨ýÀ‡29« ™ŠŒ0vp汚…êßH#ÏkˆýG#,Ú˜Žï€‡îA ¨Ž‰îæòÇôãåöùa¥#oRßõád£­UÐ^­û1HäêLz!†Ò'­Á‘ZS@x¿!¿…wžÍ‹‡&‡·ºº<„ò'/_˜àÕ]sƱ’×v]NdTß#,Eä¹Xk&¥—ß]¶Dôöô|¥5‘±_ƒÃ"íýÙÛÒ²{‡ʤ—¤¿Øáì’ Ã^p?£·døê|ÍHÖÓí]'Ò÷Ã[Gß/y¸Žîrè|˸×嘼Züäm(ðÒ¦§^„¡—€šQùÄêÞ¸D®‰Æç'º©oñøâx>›dçL|²ŸjkªôL‰Lùëå=ž ê®G¡÷Œþ7­>m;¶«ás‹éžLÒ²^n7æ³½¿­AƬí{˜€Ä%ì¶xafsŸ£¯m·6ù4vbòÔ U¥Þm…m×Eqü¹,7Â-*P‰}ÚîŸ)’[qóåÙ·¯~šõ‚Ø+¸H¥O£ ÊkµÚ!@’ÞmËïÈÔ÷&3± 2¹]+aê8܉¦(·NeŽ/q¿×g.ÞQ{.) *ï˜D<ðY£I”L¢ªÚ6êä`#/ê+y1Û¨¡¾5z””1Šš´¢ÒE¦Å–"ÌcÍ0± ‰K«°T3Ñ#0#,2°ñ>@ãlpMh&IÊ ãi¤;;½½ϯHlÏO˜¨÷¼B-p&í(}/’‡‰bSö%?ºB¸’Sr1ÁNžÈ!=#0L¼¹>µZJz?Ô RSN?<OoMtˆš aþ¶[þúS-¥E˜ÚSk²åù“·jóÏ+ÌÄå#,¤’Pßö~íäfR¥4ð¢/¿9²0Y=·n†Cùà‡ù{AŠ#Wê÷úëG>º ý€ þq}+ñ'G—ÍŒáÐ}~Ï0aüàøI‡«O^DX¨ª° ¤ÿP´€Äÿž ¸?®@ÿ#÷þ÷ÍÀ7ýÆoäû¾Ë`yêµ<Óž~lÌ#,Éíé¤MpÆ!{Öâ9zÃ_¯ÛÛå5èÖ÷¯.:`)œÎíÚ›!ˆ~Fᵞ)ð_Z0$ÙU')ÐBÛ“jÙ,ü8z\˜èñÍÈwú¶XŸEz‰Â÷úú¥ËøU0¤ÞÄñé¾d¿#ßÝ`ÖÔŹ°„‰ ¥³ƒO21§N=½ÊŠ`7êÙª”,ÛÄ˜Ç Ç«{Ï“ôËž¦¼fOQó#,‡ #/¼ÃÍÕw1ïÉ™ËÒ{»­gq¸^¥ÏS 84—óv`Uwæ¡ ŽÜ"(€~#t„‡í̾q£YEõ÷þ[îUoð©AÁ‹6'Ñûl:r¨jøê7fRKfQ ìˆd_´v½7vCeãdò·£>§øÍ V€‰è)…à;qÉ?¯"³ñéü w»º¥L0[L?½4ÝþèoÚ„8Dð:— ¡è@¬J.‚È"7@Ì\gbaÉ(…˜zب˜Á¨Ž!}[8äYE‘ s-)²6R87#,¾€\À 0›)`?Š9ùå"Ü2M…’ÆD‘n—)nPY#LÊêU çºÂü\°RF{EÜ.v2+ „°#aÁ"P©†#0E„L •1CSẠ¨!œUþT([g°fCÊIóö*ŸïݧÅ}þ„>uAf<#,ÎABÀ—DÄó÷ËÏ›/ß½^?´'e_ ×ø¿±pŸYûš5<%Š–½UtZ#/à…Ñ0ïŸAø®ûŸÌþÖ§­˜Pž–˜È̆ËI31ÉÅÔ Ó´ˆú¨–+ç˜ïI®óûÝS؃Åÿ4›g:ËNšàãÃ9#,$Øn”‚÷Y<tÔ닼ÕúÌ TÁ{P ŒAb”ÉDª±‹tɶqpˆ›Í=urjRL(»kGeN÷»¿&¾#ßß®QŤ¶’çø*g„+–!þ6˜Xø5i²WbYÅGæ¬L}WiFÓIô §Dà¸Ù;<Æ c#R«.^æ]T7dðfŽªVPô¿áxyÖ‹L_-)ì´=àv´Ùæ·.Ø[ç“°³Èf¦—OGP´¤{àíۇ̇{=Šy”´ÐúÍ7Ÿ×5SX‡uŒ¸ÛKâ#/‡Ý`Ç&…ïìT®¯1$†'çƒÈÇ×¥ž.¬°½âщEAbÍE´i4Ùf´™˜A{ÛêzN¹9*ª‘¤”UBÎÝ~j#/ÚOƒrxF¸8†sÞÛqÉp #/ÂY—²·‹º ‰7Š^ùAJ‚Á’u$3Á’@„¢~c±_l\D¶ß†i3ãæÄýzPÆ=ç]¬l%;È0‚ŠTMº›ª6¸™s¥Ï@À9»á\7*îµZT7PÃ’¤†”èe†DòÚ_1rÙÍÐzÌØŸ‹H˜´ ëÎðP>á„ &Ø# És:P¸vVòl ;(ÆΆÊ8jQ°RÑSoA×ç¶Ìcì†XUäûzLØ©»YÄ7ÂK™c[ÈÓ³‰Â\#,ÊDô¼a!äyl‚`Âf 7ÓØŽÇuã‘œÛÂÇ©`b‰ÞVzhDÔ¦Šw'š=î–Dã#,çŒhŠ¥(Ú*˜Ä¨‹%kÒËi*ØÕ¨Û&¶ñ€Aˆ ,'¹Ù3^ði1ÒPE¦¥ã\Lˆ.Ú‡!.nÍžu„ž´”-‡ªšÜq"#,ÒÑãtÈt}Ø»÷$ßž#Á‘Ka’\ÕècÄáÉÙÖñ$Øè‘bÔóîMÌ|ðä…ëÛj'9(é³R†öãÕI¸Ç¡õuXgwKI ²m±²jÅ_©­õWÕ%ÃÁ qð/;ä¦já”óñÐ2lx7O#Ó°"¯xÖE("ÅÎî4 zÐr® nµ%,¤É$Œ·ž*˜RZøT)2Nyl[ý•¬Dû+ï±AyBdvfÆ1,˜H~ØCDÊ#,ЩˆÔA Š$ˆBlmÊÛê¶Þ2ù-«æ](HU ÒÁ9[@š‰ìÖ‡ž>68<xÐ#0ˆ´…QZíÙˆDbÂɵOŒlDçËlHFGœ5 ë‡ Mùó·¡½¸rF1"‡ŠŒ¡6Hjƒe&÷òW]¤RYVºˆçÔšY.SK€¢¶»‰lå o‘#0$àÔ:Éa("ç @Z<ÞZß–Þ9‹Øtr#/À ¶ñŒI#Àhõm©BòF;ya’bæ¬Tì×+Ã5”°ãD(‘H¦’4 ïçGJ³§°×SJ„ŠÈ75D#wG,~»<;1Ö±,= b˜<A4S¿u'˜ëÛ› ˆ÷î{[ì¿CÁÜãë(ú, Ê"¦ê¯xûTIö«©ÏÇ{ï™Õ]¹]K„÷¼›3UÝÒ‰h.…Ìg‰B€4ïš‹ç iO“œ' rÛÛ15§>á,KÜ{†“·Çf/cïk…"tÒËÍ­:Ô(ƒàE(#ºZÓ:ä†0¯U+D@„ ™ ²ÈpXd]ä#0¡ç·Íˆf‡3ŽÄj¨#0”O_§-]ûÏ5Ì–æ{B¡ka³~O§@ãß”Òð–sVI$˜NÀl˲! ²+)½Cü¿9°®½–«ÇÍ¥Ž2ìwbIêöwr{™N©Mn¶nOyGáåÝŒž4l„`¹‘°1šQ/]Ô‘ðïlwÌz^¾Ý÷u±§sá¼4=¼Bá÷D¨ûºÕÓ­Bá“…“·ÍÎó$œ>¦üNU<¦Ô$À,&ù4>¨gg£ž}Ož`æÃ;¶è@=.¼Lû·lï;+ÑÆõË£N8Ümàü€¯Fë5ŠšiF…&åø|ìÀX¿t¦7{Øê>°EâãéÔí³kÖÏ.šeVî83ÃÀ¥#ûfö†Â?ov&mt†wÙ‡nŠ¢á;¤%óï>;Ó lUŽD¬AXASêb@HH+›û#/<((ÙÉŒ½ºí!é`yÇ2ó î#/~Hû3ÌÃ-}¹NÚ6×nGæcPÃn¥°÷©ç“4V-ÒŠ2Nêâ Êf¬ç/‡‘f0Z ‘P›Ñ)PAÅ,˜&™ï‚ß÷-:­xÁÙ=héÔ³sÙ]ÉÂ|vÉÕ]¶ÍKéZökjlÑf½ÌûŸ7¾âÆÌ;%]ɼè}†žV½xLó" 7¤342<.ÂLSkiu>d#0ŠÊ§h;q}tÙ¤¢y°ÎÙMÝ,‹z=>/õ¸>’otÖ¹NZU@ŽÏ̤¾±^2û1ä¡Kg«}“èDŽǸ n³`ÏpjȤF0W!‰ÄéK7$Ù+¶Š‰ÂßqvŠž­u‹ jM•¤ãAËÜp7Qrs‚fšc½¾ÿõYÙ‘\·IT0í£¯R¡”0D=‰vCd÷màchÌåTªV4`rúÔM¸…˜`A<Ê#/ѯ‚@‡…èbÁ¦ëÓiË›èM²;¾û#/%9×C;Æ ¬íØÓàž„:&Úm²@îiÒ.¯íü I3`n836ì­$:'bá|âšë›;8{ï ^|œˆkÌ3“ÔŠÏ8Tv–´Šj¼†o¶g`3DvåÌ6UŽÃ.5ÕÖRã1ÈÔ†€ï"< õðºí#08Ç)P”G ƒcX›§SÉŒµ“ÊÐʶiW½ö\sŠjD4Ž¨B^pr,æ@!¾në¾ÖÞ-#14ZOZê¤ÒdÄͽµôµëWè/±×ËÆÅJZ¡q“\XšwÔÜöËRðq*\‚I$ ºqÃhâŇljô—£ð†£õXÛŽûÔ÷ì*5]6½ÙÞ|ê„ê¥ÑdEÔx”SáSÁd¢OJQ³ßUöŽ47a^8;I‰šO¡'49úŸ}tÁ¦zx¸@cßç± 37kÊî°hÄ•sŒà´¥ýŸ,!k“8%Ð;Ãç^£¢ÇGùÙ¦øTŸœ†êÙ@q›IAÃÙŸ_-{5Ã1uÙBþº &ÁåÓÇøô#/žÚ:ì]ÎDÀ}ЬJÉóÞ‡T•¤+Bé]u{*½ä³ÃôþLÃ#'Ñb|÷ìûjx€&8€&×®Ëá ¢—4[—ˆuß<B-ÉÀú4køE€ñõ“ô#,"âåTó0ó⟞’¡ÌÃ'Ó~φTwó;EsÎ;á,~‘”•ÝÿøâX¶³OÄ[~ßs#,ÅDßTÊ&{æÏ)±& Àú:¡ËòWÛü%ëî0˜aý³:@$“ù¿ÂN ²‘"€eËHZg‚ü_Fµ©ƒ¥I‚g"¼ôæs`Å’Ò=‚ò•ýƒ]]‰ NîÜ~¹‘Ñò‡ëo™ôôržå«,û³Óu¹E/ò?˜Ÿ¾Wo¶õ^Ö¸‚¯…uCÐDOÓðÿ7£ÃóþéíˆRïÖ$/ûÁŒéNUËIbû¨åZ¤çÂ**ŸÚ‚µ-es¥ç©=#‡j9ìcGþÐAE0íî2;´Ï F#,$o£NÖËVdŠ­U ÛW×4³ˆ3—ïhÖq§Õ󋽡JZGœä÷þÏÍ·Wå¬5BÜÿoߌçû9; ¥™ 6„ÍÏËããàž5Úy#/õ¬Þso÷°ä“ú’’"DCö4ÀÅ Ýü#/ˆÜ¡i)}Uàc þbñ³ðÚð}‘#/HçT?è²~­÷ü?@®‹ÉtÓ#0¨Û”ÃuÀÄ„{Ô YD‹eE]Ù­mÂw¸™æŒ.uBsºëC<PBÊç  L¹Dî=X\à€­$©åG9îÇE–g‘ö;ý-ÉÍÿ[‹Ôönö¤Åµ õtËgðs©+@7õcVïÃâ#,›sßðt#,Hg¾„2¢•‰ç>»X06M”Ÿ'ÚÒÅ(¬ìßõZ{qøÝy|iFÚ§×3ÆèÄÓÑ4ˆúœÏ®zcBzHjëE#Y}ÒÏ—»ãÇ躞tGàU)n1ïå÷I×¢ Ÿ&˜0攲,ˆBF S"Pä/ðå´:?ÛÜwøËï8y¯ê´ËÞ_º»/Ó¥_æuY Iú¾/E‡º'„þãnZ˜ÿ”?Þgµ‡Ñ'ø“[¥ŒDH±d?ÊrãÏá§ß}#0ªæ-]Š*¢%$âA‰Ç>L*ûÄäág—§ [ò#/6üü6ü?´ÏÏ›Gˆ½=™úçZ‘uT<§ÏG§Ømá˜qñxG£¹Âεë/#,RY—p#, ¶ ÿA¤›_'áIÊAåîߌØÝ>s¿ù «‚˜¶#0¦J¼fµQœ¯äª‘{”€Jy¸6®n#,@š¹;l)cÔˆ…s­æ#0;(³îû>€äLzIëì@ÀI!’Œ.Þ;»»óäÌÀó!LȌNu£¡\ÁÝy¹oÓËCʼnîå)­ËßM]rˆçÔŽmc,ÃÄ#,jÀ§slÉ^ß:‡õÖ?%ùJ9GìÿßÁèïÛX‡©ÍéÛºžYŸáœöW,2~9ñÜ]ýeµ™\1Š6P9§¼øöxþlÄNúbÃ?„ÂVE9y{tp…ÄŽ<íºhø4È• #,<ueE•T|¡£Óp~]CK⃇> ´xÊ'“Ä•š"ˆ|Ù±–¦ñ6—hÔˆˆ3¢"6“hÆ¡¼€ƒ9Âj#,çÔƒûŠ?Ë/‹Ë§VyX©<|T#/z·ZÝÛÌ®¯u†{?—•êííFvÉZQDMÛ„!ðýVrÌœ¶r%ŽQ#,fÀ³#, dÌ@z±§‡¯áá ÁÔ%».W ÔžÉ2%©ìP'¥EY²#Hg³œÊ¹Ä ®tÎë'¥$ªÆî² {ó• …;ˆNö ØÌâÅ¿6˱éßKá塼á}îÆ̺-ÝL#,1÷ÏX¹˜„±¹¿ßw÷í%˸Sœýó‚Îa1#/xÔ‰éQzÇœJ††•3•™¿@›<wû<Œû/¡*R¢,‚Õ!ADB!#/ì@£<öþ½æëì-Ë'#, +°ƒ¹o@œ‚.¡âð® ž¯ 5Ñâø“3{J0›}Iø‰ÆýçÙøÏ>8ŸŠî™½9{‡§å2¨€¾´݆ Àx Üšo5œK[‹ØlnÊ 'JEAöîÆÕ²Ú9•UÄÒ¹“œEWÃ&ß½²WñÜÖ·çlDˆ>6G6⦇™¸{?I5)üWÇâ5t¼?÷ó£öuòÏ溮À#,@ú€9m>']£SÔo6ÇñÕÙAß·Uˆ€L”ÃÍíP#,r9úGù©œt[uç#•[4éŠ Ž¬¾R#0rŠsÙV“ð(›u#,¡ÛsÇ tÝ”A#,A¿‡ÒR…»þ9¾#,ÃÃK# z3z¤>Ž?l(@,ã÷I=N›Ù"„-Îûkú[ Êw-6«¼· À<Õ<òôÁÕ÷}]ÑÜÆX3ˆO#ÐÏMo\âpµdŽ©3á–xHêªül“ðM•sî½eôóÖ*GòæzIB]}öÖOKøÁBà·˜9N©ßZí—çC˜1ó°‹B›š8Nlp#/ðºÉÏØé3‰fÍ%@#/¯Ûqñ‰I|öxH”‘äŽ+W‡Š?Óϲ®×{x{UÖÓy®ø± ¼äq$B?o™:k[˜o?±ñý2¦0Ý%ü–Äq¶1UTÄÝý-ÅÑǪxz½Û¯ïÁR©.?¸£­¶µíO3¢9_Ëóû0R(æ"1ñŠŽŽNì}LguÛ올×Ãâxwd]pÿŒNsñ1SYó­FWÆtuN¯j'N|ãmIþå '~ê˜mQp¸æš>вÛk(tÁ‰@½¾„|¯°D{6y«®–MØ*ù¼4ÁïÝ1!%rk`#s=¦/{ŠSY—C¾H¨ç‚Ð ¡îwoR3o=@äGÜh)<K#+Ú£ó¹lÌ°‹Z3Éï¼q¨ƒßeN–!þ#0`JÕKé3‹w©Ÿ×ª¯ÜºUN/ÔˆGºáØ¿„û±V;Ô÷Æhòçh;[Âß&ó¯]æð[FL1Z²mï(ÿg2¾:ï6د›çàq¨+ŠÓo.(å:mÅÔãÏ{ª·N9ü"eŸ¿b¯uu“ÕÒªŠ'¦ÄcÅTÉø?$æ±™‹•´âè»Åœó¬ôμV§TÐcÚߧ&"çñŠwpÓv3‰âû_Xß_Ã,XeNbÑ#¥Ñ#/¦õ¿#/|:l*îö7æmX½Ø¢ÑwºžžëçÍ3Td7!1¿­>šü6(?—ÝÔç„þ:ôv¸g!K߃1²Zôxç4zç–âü4,#,U#¥¹íXˆ3®¿=jꮞÊVžAÀlæX¦¹À®hnó…Úò¢}£ì;m¹§ĺ©?tì¢÷ýðÓTf$‡K¹Ø‹ëP(ïÜÖ©;ùë+Èãn Þ{mke<þ"êjtœSPô_¥ÎrÙwx4§{çé'ÛlëöÂÅ›7çÇÎý\J"æeðþoŸŠ&:¬âƒÜCÃ1#06õ´Û®7Þ¶Î;LäqºS®\"‡8SSÌ“ÆÇxÛ(2ïðwç#/?ŒÜäÅáÝ8éwwB¬whðk}fÜÁ„äø?|jÎW¶ÜfŽÍ#0´¨5sÐïü6¼™·¹8mÝù¿ËoᘇœeãšËkVräAõx*_ô¦<ŠíÌR‡´¡Å³>1ÂU!dzòÓH”BXG®-#,¯Ã3ø©`Þo°Vt>Î#0”¶ØH#/2ƒb'#09ù÷k¡Å.ÙJ8ð¼oŒèæ2Î]гڇ4Ý#/•¼Žw=ËÑ\ŠŠ ×Ç;l¥%C2I}JàHe<¿Ñ Âd¤]‘R£ž•Èá™×}Š²þQÜšÎߺ$·•<<ÊLé茾ÁMT¥Ø•3&^åÎêÓÓÃF—Éèþ˜¶>É༟­Â‘úÄ\Çß«œa½cY–1’¶Nìçú{P_m|%·û/Ê6b\û¥ýmûb»ÎN)ÊÀ=nŽ ¯+k·åo2­j%B¯h/X ¿!ÛþdHƒòM’;S û”ñ³zC@#íòôò¥_¼’-¼©í8>̘ð`»j ÓÕ¶Q£[£Ò÷H ¾ªÚãG̯Ë÷áw'èèÛ¦œ,êŘmüzD;ÔfO+0.ü>²…ïÏÔ~“éÇa”èV¥Œ*Êú†¾5<™yü/WæVe_ŠWv,ð7×ClšYwÛw¼2ù&?uÐ. dÇÙé[|ß #0H6œÔËN\«Olåh‘Ö¾OoÆ,èíÏßtœØO¥5@äž^¯Z4˜JJ`vÌ…ŸEpg4ÂèÊ‘¤ªuåO£ãˆá:ûÁkO?/Ю]µ'‘fc~?\ Ò k‰i׳çۗ—¨€/°T…êÕtÅ¢)3‡3 ñaRö°Ÿ PÞu÷><+E~TP‚–ãÝFrqu&¿/EÃl8Ë>ÜÑŒqÌgðúõVš>"Œ:ÒXyÇ™ßìé|àκTNŠ‰º Ã’ßÓ9­GÉGMõÑ:÷<ÇHgÂdþ0¥g˜Âúàp¯=m; Úœb±§Õ⦬–É׎¹ÄË>Ô'©<´¨hÁîx{Ü}møÎQHø £Ú£ã¤ÃƒpíäË{¢”¾"¤‚…‡"w²tIÅþ*ùÍ»ÇÂSÛª®‹r_d{=’!˜Žd™úÛ‚塽ºÂç¸ÃÚE7gMCd<¢p°#05ŽèM´›AÊèÂ’„ô¥²upÂitOJ‚ƒ#,ÔVÒ¤ÝÛL<}||aînj{ÎÎùV0}ŸšÜž„Z§ÓÕ.­£Ð— ÙAp¢²-ziUµÊ’DZ½0ñ@—Œ1‹TY!„!0Ì ý #,¥EΫ¡‡×):`v¤F»xmÝë’ÉÙ÷^ÚY¨y¼R©õîëƱµõÎ^ ‚«dP({ÝÑb¿—o¦aÛÓ¯m?\Χí¶x¾™T#7³×fLÅ#0¿rÑŽ°Ó¸w£ïšôÓàò§í/›+s¡PèæË?îûŸÇ}ZUÃú’í›X éahbz†8ÓÛ{[qPÞ@ ‹ÛÄýf¿¯øÈC ‰¯—öow#/#Øþ;‡ô–2ÿ)ÃÃ"È°àKq%Óö],Aç?uJ+¯í*}Àçè©YÅÚÀÿÏÝUo±ŸñY¡üçî/0 ñ]œwŒº^õnÁÑp$"°q!£†›{à+ŠØ@á¡Dð'¬gÆ÷T]$ŒjþßüCÚ-ÿ•3…+"7/ûŸü•­ý^ûþuUÓýëQË ïVI;Ó7ëW.ÁÓZÞ‰kÄìö‘ß#/Ä5pü4¸yº|¾£ùt\¿QsÜ>çâ‚ùÖ,ZC ýãYD}õø)úäòäþ2hþ'!vÑèårjñŒ#ﶿs¶¯Krpëz\ô‰½ÃÇ,¹Æ¾õsìú>Þ|¹s’’L9n^f †|ãÅo¸èx©Ó|)Ï<£ý½ÙéWõ”åX¼>¤öþ”‡ú½…±›è0!7º=x­ùp…™eú*wW_Ð{Ý©à*§÷´[¥¢Ûñ ÷!ôôzåÃWw#A@f€üW°@CÖv$ã3?¿éҒéì¨b¶]Ÿ'Þ¥(6I샭¯öû¬ï­z€Ñôí¿„ôk·Pñd |$!¾¤øBéQ‰Ñqw½ÏiŸ.¬áæôÉ2ëÄÂêçßžéyªÑpÀrÁƒî·MëÌ£Ç%‰7:n°>€¸'#0×1AÎ?g#à‰û>ýw×u»‘Ùò)É`;Ø#Žê ã^rô’þTZôG-2ïϯéôú#0‰ë íÓ™ÌìÙŒ¨INr$*TUf8κƒIÅã_îöYä¹õë¨tŽÿ‰¢9€°€,=¬¹lJ‚‡owÊñR%¦Ýxc=ÐðÛÀrNÖyacŒ2ˆ_q^å#0PZTPÙd¬¬ü NGAÙ¼jÅÈ+(-T2*kù{¤’$Hê°ÕåŽw1’·8koeßØý{Îå”­þ#0#,ôÂq7¾{ɯբJm©JÇJ'ƒ¹¸!·õ­ikß»Ò+/÷àwÒ~謻‰ô#0 yÝ)YÆ~#00’Ð#0.ž×º;’©j¥Ç¡Q¤hEKÍÅ›„ùß”ŸÓÚÛ#0dɾṺ3ö~|YA¤}«9½íPïÃpÜM3l²¸šî¤Ça™”hpßYÇUˆJßyÝ‹ž_&‚™Ñ^7xHß/º,p†“éºC˜óJš°=]ø5tžž\qìÇA˜LÅ:Ól¢„©:sìG;yëˆsÈ€¤ïÀ]åƒB@PïÁPhÄ¥#,·mH/¥¼½¦°œ¦°¹Ñ*xdñQS5æJôrTú‡¥››‡FÀüvl@öA§/_$øoÒWû^øcSóJse Žº¥*›LBég–£ÓL×X3Ü ¿|êŠ< €© ÝsäÒ{= ¦f¨õQ9¡Øø¡–yŽw®]6²jêŠú˜0€^Yéd›øú#B€gvÙ»Bä6¬‘L[ÊÉëä¶Ç ”ÙζŸ¯î[ Gp›Zä¹ÿæóxýcÍ«QOi뱕¹=}ƒ•ž*A$€Cñ¹ï†˜qH«»Ï(Á³Ÿ#/çä*µÎ§êÆLo¥±eý}‹8ÑúTJ¨‰^¤ˆÈöÈB×'2§˜¸qé)PŸMAÁñ,9Ø<ÛŸÎOÝ9ár9ã™:¶ºÅtDÝ¢Ï,nùŽyôÛ§ñG9ÈoÉrŽªÍÝ$W®ªU'I2gt©/㶑³kâœâ Ùz6,•4éœ!uf¥Æ(”°8yï{í@ñ‡—5Io|päYÃ6ß7hóˆašY—@É…Ó•Î<…r:ƒ-{}³†%lÉÃKœ,gÃ1“ÁZ¡«(™“ãöÜ{-–KsÇj;;ïÂqUñ»ÏŸwÑÀÜ [<‚iŠK3¬LAtâw¨Ñ@å#,º7µy‰kÕ—E¡˜™ØnÑ>?½F6?Ô9FAŸlP]§à†\󦨵Z#,аYkgh{:¥²qŽ¬òh4F#L§•²j=–ég*ìa²¡µ³]DÓ©¤ó„¯O¯4fÓ•†2茿clòÓßÙ‚2¸Úáû;'ªò?ÕÞIŒêI6õ¬gxŒëæžoOE+NÈ•Íù{¥Ï~þªNo8JƒãïíÒô»z§öã“rI™& Ìõç·”ÆnyË{ºqœ&ù]¿má×¥ïiSu ÍUд¼Yy‹,³-ª¨àd!#0Å—§@MIlg‰A›»ƒp7ŠÕHiò#L†˜¼[hØyÛc†Ì®³ÉsÍúGØ]œ¦[ÃÀúÌ?{w…rõNÓˆq†™H)`%\‡ñkûòp›{{ =ã‘ù(„7iô y+cÀÁI€ ÀXžž—²¿¿ÐÒ¨{,ø#/ÃkÚŒG*˜[C` ªd^ŽH²²õ#œ¤`fFïÓ[(æU¼Û—d·—Óðøã/#0)j”&hùûjcƒJNº¹ïûÞÖÚÓ¶i"@ü¾ùÞ‘øk4°ã®ß®SÖãdé*$õN:{‘Øó‚ìœv?¸„Å®&õ5“¶x÷°ëÒxH¡¡žUaø¦ Zøè2åÎY÷”léˆlÿRºi:Žyc†6zøS»Õº<›q‘“|­Hóù<Ôºg¼GC€ß]˜ az°G’ÑnC<‚ÉvJÁç×#kȽd‹TÀ{~6\¸[Jzƒ” lK\ÙQÎð°Pqõ9¤¡ÏTXŽ8H\ôϨHðŒ½«©T` M׈ â3³'’䟦®´ñy4ݦI[ êPw[#/ñ ©Ò~­%!T#,Ù ¼;}5ñ ˜I°#0ƒÊxyyŠHŽMJ0Ty$„’O,¹"#,ˆÝU£®iâѧÈ.2«2ø»ò;÷˜“:®<ÿÖSðz£Ïk!m×­þVu#L«ˆ¬£j$ӻ¿ˆºà“Nt^¦ºâ„#,8ê‘Þ,Å„g®a:;l`Dõ k\LSÕ ¶N#,Wp‡&†íñ@Ãc kÀÅr…zù»Úú¾=lëëÊX‚hêg>/M%:aÝ H¢ëáf«öãìiº¡Þë¿{©­–Gü7½:úÓé;~#0þû›í7C³ Æ^ú%ب]èç{« îZÛ›—s`“çÉ­Æà×-m¼tUÎDHϲË29à»P!ǃ©xí*bQØ_0º#0µÉÇ â-¤År”90®â)ž9©¢È±×pWå~³ ½ÍžweŸb˜ òÀ=ÕådKIóÉE.\„„nîwTk©sÚ,$T È©ôgËÉ|­,Ãn“·LÖ9~=J¸_=A[-¢šž2!r(PjPÚ¨#¸Î%10ǬbÀÀ8Šé–­ v‹oÂMsäîëã4yËÃ#0g:AhÜyÞÑH»SÌoæ¶ù ª¸#0…Ö¤ Q ´#,Éߟ& # kÕkÜö¬ ¡#0ÞŠRÒ–­€Êåv–¾¨Úy¶-vmÁœ~¡¯îâ­Œ‹Üþ_Mƒø–"½—™®?qÜêty6óSwøD5úF„¯"X¼ÂE÷‹lƒÏ`…‹â5™è7åæçA0z`§ŽTEî;Û8v7ðìãùY^–|k+Ñ{£Ô}I&ÿsÕ>Ýј‹ƒ(šKhÈPT4ctÇCûÆ5žŒÃQ—:«ÕÃ@·¤0Çuúvn·gç®SQÛuŒ%£Ñ#05|Ò;$±ÝelêXÈp­v ³ÂöG¥œ®ië‘ Öûy„Wâu΂{zG}pÔœŒP2—0ZÏ„³sàñV¨_*êpV+AAW'?]$•NÎEºNt3‘VÈù\¢Á–ïÁ0/×N~œ»}FüXú–›Äž•˜Ö~gVÜÑ›=yhhÛNfäCc××6²*Óx¸†EKåp±ÞˆèÜH—\¹ÝÉäÈ<ìŬ6ܽ#,D=HºŒˆèé}hÚÖ®ÊåYª(§—³CÚµyÙ†¬ˆ²Þ[®=Ük/}WÖ^EIŒvh5µíœf¶mhæ\[EêE·¡A[ÍË*C¢qxF!eTÙÅqD‘οv<l0h(ÉÙÛÄxoÔB&þÑåiSx±í…w_dŠKÁÈ5b¡9JC`¨ãª«ùrÛ…õâ,Cƒðƒt ®|ŸÙ‚Oo|Ù·ç¾6ãYs´Oœ;]»“ÅðëSÚ”#,&l£J×Íû-ï¹¥ “ ¸¤kÜgât®¶"2yŽ [Ž#BžóS¦·‹n¼8ÞUÕº:áœYŠ ©`Ì»”Y\ØÔž-‹'š¡µv”LF˜áZRÂoªÌ*±7 6‹5¼Hæ­;ªp:‰¤nQv½0vë¡M:!™3€¢±$Yh# ¿AA\é B½¡­é`n=¾4^þ¯9g÷¬Ozr)5±p†Á€†=È1'låM'›T·ë#,¼"ˆûøÝx™§gê»s$ÖzÀFGúýŸ_íë¿Ñvã©ÎÀášâ·ªB›©vºÅs”ñéè†èiõ.úZÊ h)¤!d2ÌþW$#0·ÔºšˆoÙÑÌË€¾‹]dúN=º]ˆ` MÏðÑ«ð÷{‡¯È=>aqÈ9årüÁÓùh£çÌ $Où,³>è.®Sà‰Uj¦­ã½FX÷¸t;¡‚©$„a¤#0ekªêdyñ%Ã"ÿM‰^ßÏëïÅ– *>³´o#,“øüUXîsÕœmâÀÂú·/&³3´e`ÏóJub¯Ëõ^^Û:yñÝølÿñâÒ¼e€Ç~À[°^`EÕìZ -g ûÅHŸÉÈÝuÇ=ááIUA‘ÉùŒÒçá›/Ê#/ê±ož˜¸ ¿™ßuÑ­#,©ÖU*è)Žã«ýLõ×ßþÀ=ø‡߀»õÍI?­è#0Ì` #,¹|ú¡¨²±ýúA#,rÿŠQT”ÿ®8à…ˆv诚üì$v ¼#°‹+pÌ8#0#,©þ‹AÇ]³ýŒþRÞ;6=½–8(]¹Û™a·û¦û&‹`ÿ…@£ò†. vƒàòêFÛÏ´Ü*h ™!©Ö܃¼ð#‰Ë˜•éæmO«ÁÁ¦óoŠxbPªgÚ7¾Â¨…~#0QƒPÔÖlÂZàòáÉl"`aà×1±íPë÷£ØAÌ÷{àúŸ’ýŽhƒ™¤=BëÊ+þp°/pwÀ»ƒàN¡3K-#› ܹüw¼/’l2¶u°, èÁ@}FÂÙ!:NÍ«’`dže=kÐ8 þÜTûTýk¡Ýþ=½Ø)½2ŠxÐ=¶¡Î¢±@¬Þòêá©È#0‘!ô—µ±@3¼€LfWœóœ›ŒË’HFóLê¯W²Zé?³Å- W#0#,*ˆg#0ÔJâ:5£êþ÷ü>ÿ_¶ËHÅOßbÛ·Y-õI#Ûçéó{Fåº õ¨c©îbæ?Žpï Rb•U×¥‹¾õ—¦$Ø}fÉ kRSy[ãý;x à|(`/¡Ë‘Ïõ’ÁŠØiâ>„7Wå_´¸;¹ë8jÕE£4±Ì—цî8$Ö‘¿«ù#/@Úka¬*p=ˆ§"|6&œÂ”b‹±#,óÛ0Ÿ½ê)#/6F”ùò’ŽÒ2~h„…ú|©ªåfÐ<åÉ8u}:€æ#,æõ'QÐÜtu¶ìPÕh ¾òLõMš£jüèn;ôöœ/iãr]È‘º–à1ÁŠ#0`ÂÖXp|Ào{ ‚êðRŒ,=>’Ù §0çs: Ù‘#J±¬u-‘4ˆhUpÁÃÄl-Ð?"Oëý!êl€^/TûkÆõpÖ’}ßX^äÿVϾ^+!”}BXþr¡€xÍS†\BêrŠ31L•ý%ï§üp?g€OÔc~Ž9Þexüñ±ïò®¯âﺣ A$2­@9¬IpîˆÇ¹æRÇý›vÞ­g^FÂÅÉèÍøoK1!Û@‡RHõpi2¦Žtµ¯÷——–¸ê·ßvÝ£ržÂ+ !) 4^*÷û÷êr#,$#0"Jé>NW¡ÔyRR°Àø|}”ÂU|mòñšŠý#±þ:Ûª‹ôŸpøÇ)O’"“B+X4o‚ H˜:LÌoä'#,„\ Ù=§· »&è?lÎjïü™ŸzþÞæYøl ­Œ‡Ÿý»¿i!Âf¹´u2’Œûr_*FfÔŠýĨ}~Ó³ºht @3ü¿ÈURò¼P¥1ýÆåA%S•.¼®{#/­ïÕ;ji@:ŠptvÈo6Ô¹r~÷‘ÌXôƬ—kçØ`2÷-<ûâ¿L9²:I#83Æ1nÉ%‚«vî’I$,K½±+÷{wå› 7—}»àvlGgµápÿõ˜ðyð5í#/«ÒÚâêFAB™oJ¶Z9<ŽL3e#/Ѻc,¥¶b)¬¢â3>ÔñŸ‰ðú¹×¾}ÅØþŸÎj@¿Ú¡Äæá#,Heþ=¼ŸõɇWm¥Uä`þ¸Ñ×·¨¸“CŸƒa(þæü†-0Z2_¹êVoä~OW¤ùð¸Úñ¬‚‡‡ã£²€~ŸFµÌä?0rF×LK,}îñõþïä õçCÆ{NË2$ÝNó"ئBË™Kdê»î½Îs«=mÎ:iŸS¸PÄ(6fÿã”4~Y/òêo-¨Š0DŒ!2Iœ\älî*--`‘CŤ#0#,¼W¸°|xíÜȉƒ¼‡¿Ì˜.DC!Ë¡£¡Z ¾¿¯ýýk󫧸ˆDªHJdW£©M¦äûqTîœPêí!#/ÒËÎKQ}P+„!ÂáÖ b*š<wµŠ+ S>ô³à\5Y#/]öçb’z’•05¾,>YÅ^Eí“%°ðXmÈÐIR¥9™%0¤Ž:Ñ’˜1èddh Mž$giÉíî+Þ±šÄ>²{& ³É¨…#5ÖçõpUô†³s‚vyñ –=¯I‘$73y3Ça“ÒºS‰u0bÈvHŽˆ}¯¿öu~ƒí~ÎÁýØï½tWzèü\ó’‚Ö´¦~œ#0Ûq;¸ýÏ8ׂßÁÃt÷Þ²o™!ŒdQDPÍñuUtfÿ‡Ñupã™Tj'û. ‚ÑèqÑ 4ÑUWB•b#0ºçîŽÍ¼fL®Zi#/dáÁÙ’H¡$ÍÐÛX¤ÅLÖ³V2jÕ—VXWuVð‰ tJÜ¥#0ÓH‚Ejš”Ô²_äÃ8ÑÆ|7½ã«,hÑ«HÍ¡­¶Øþè´G)–\}ß j * „‚§8÷d%Ęá@=ÄÒQ0Þ^çSÙûmý~gO—ºDêˆOÉß#0|kÏ™¨ï‰"% DÒ•€­D šóƒù{5tˆÄó˼ÉI~µ~ýJZ,;¼=©÷#îûÐãO¼é@–‡àC£ÕhÞ„CúÜÁ5 '1RýO!ôr×Ïä?’ü¿‰³{\àO±<·ÐµôZFk.Ù¡UÞ®Í`Á$A¥²ŠM£çL“§þ¸Ö3“BÉ[Ü\$’I6Ä0< aD8„êîe%5º&WÎf^$ aB‘:ÓÞtìmžªoËc–SØvøŸÉ9jyâ’)ü7ìÑ{Ìjç×.Ov"j‡dþà».YÌðžÐš…ï*Ÿ(sŒ‚¬¦˜½~½tènÔ¦æ‹R¢TR,$j…-óáÊÖ8|`(p2ú_iÚÏèR‚Ï_´Bôlêí ìb;ÂÅCÜ{XYûÅ’¤rà{Ü$.ŸM¹òjoÎzmÄ®úÆ÷}r{.vzŠ¢šZ¨HBC\MÉ#/Û€òš2ÝA²êÛ Pß[7uÕn€áe(³¥&'âø|MùôµÐvƒ§Ô‚ßÄ}Q8˜ý'ã9E{„ … ¢†ÆöÉŠ…§ù bôDŤà"…Â`‘ëõ…ë"#,ä¸ØMÁÜ|n­ñ#,£@]ßÀSæ@\,üuႧ&$W €è»«Æ &·ÁÞ€¸–ñ[»pþ#éáuà‚•CG½EEu|íR|ò¼ØÞ—¬,ùèTh€¢_nÐO"ÇAõàXÅûóÁóýú5>F_=èî9ÑŠñlú†ÀXaÑïÌ5¹"„ ƒøŽa›AúœN½æãìƒã¾á1§;ºÿSó:ÏCZ§(º±ý“òëu×½?x‡èõcÜ©ØEJH‘Á#,n|;a2Z5¼<lHŸ‡OUqðwž`âîhÁG Û·5¸jÎÀ;½O§©RÖŸàuq6¡7··¸ò#0#0B)b‰ôžA5ø<ý§5º°îUa‰H&¾:\èC¹9>~†÷‡ 2 RÇ̽<éI(ryCoOsUó¡óêæqüXýùû öÛä?ÃGàoˆþƒì+ìcì“=¾ûcÊc!X•Œh|Áù1»ãŽ‡wÙú¼çoÄ«én bSbµ2P,Ä$=8FI$žÐ Š§ }©wíѬV"9¶€,‘ÕæüâþDŠIõh§\p5 hiÛÌ÷Ã<ééýGoÑA‚=jgÓ¬j-Ã[[j{ür¶C܆73£nu!rwæ'x…xýžèꘔÌÖÿÊ}ΓÐPh<~ÀÍô_wÃ!B&Ý¢iUùã<’2M•õYPÍCxtÜCØtל#0næÁbq#/…ƒ™jºoyÇð99”x#/"‹·õ Áå¥!õd«"jÔDJGÊJ87"A–~¨%ÄÚªšPN65…]4%2ÿM*'j©‰IëõüÒˆžj¢1ID© 8êösæçša(Á˜Zëäm›Ð‹c{ Üô˜ó#/Ï/OJm¿'")E^ü†“,0dI‘Yëð#0 °Ù?YëOÐt—bXÔ!EÙÈæ]‡©„ªÙAkÅT‘%lÕÝЯž»Ÿ£¬ô¢xÐ&aòÉêµp;7ðœÃa|P{M§f¬;ÿ"[7d¯5U(½ÏoÉâÿ ì¯Ñèú|BݧÁÇ·)+3lb˜¥~K@B¤Ÿv ©TÐäŸâÊŠ£U”+ÿIÅ(Ô5\´¨8(©u½ä àÔ&i‹¶£.Ù*©IU¤p#/žoŸgo„ôžwÏ=­ø¢ŽÅ0S’Ñø.—ÀûÁÒ¢[ú>KÊMm†¦(þäËÚî]æ×Ý(T÷CˆÇÛx ˉÇîùÎG¸óŒªQ¤!DZŸ;ÓöÛˆ.A6ܳ®”éúƒÊ; ˆ]ss=SºK’ñ.+ó¹<ÕGÒ »ÒÔDO,¤iñÐÝÆoéä8#,±í.X,X°QÄ,&®ä/ó˜=µld»°°9™Ê=Þ<ù~¡û?,߸:|hõ³ÆÌ$³>Ô)}“x#0j\¯4Öóĸü‘‰\qœˆ#0º¨-Á#/CºOÑ0FÀ0Ð|aê°3)AЋ!›6÷C=¯rWYï[E«õ–Ûá’B#0AÂ%5J‡b‚dyü¸ª!Áƒšl5-~ƒ·#,Ô7žÌ½¨x‘±‰ßá…Ñ㑆E&AH< !ŠÃj­ ê€:oµ#/v'œ?Jvï4S#`Qñ£ad  æDbÐZ „OéG#,õ}<¾Åø¥énHõ3UŸOÆ!Å~É%#¯gêùîÕôSéÜl6B64JfQ¦úûÍ_\Ö½o#/«r3J„Ùl¶î¼ó.µ<ºçZ@dd ¿ÈoNAåóô}Y_àÞ< Ë¡w×å<Áë}½j×Æ៵˜‹ô’-zOq#,°d¯'€0ØjÄ3´Ã¬Úýº)VCôJ†Çj"˜B Š#,PL,àÜáRŽ#0CžÓ4,D çG,-d'ƒx‡š¨ºîäë¼ á'›üñ®¼« ^‚ý€´·C´h&ÍÉÕ¹?<ú褜k„nì “t=WSxè(ê#ÐõÚQ·²![‡“AÑÐŽ®YÏLì·;}vÔÈmÉã#,Úb›°¶psy­§²â=$:ºìcp•ÓU3èÁ,="|?Ê®-Ý5R•QE¿…`©ü ~ŸQñH>Òª”òóíÉSÓÖ{ðr¸„¸<=¡Ò{}2ìù©¤üDÇb6ÏèDÙó…ZZfi“ 5!xXFˆCz”z–IVI$}\û˜?f#/áÛ«ZÐ0ÒÖ©‰ƒ2¥¡ãd täBGÔoâ|8ÑH|XÚG¦ŽŒR:G,qqBùtþáç:uÕH*àš`„"Ÿ (|HžŸgåÑÔ^L$'#,ìßµgaÞ†(Dh0ëéÛÓ†?}w„5ËGÈ!ùæ/_/r¾¾Û 2ãJx¶1®‘÷ï…hŽaßH¬Œb‚’Žäô°Â%´ USN½t±L‹Hhq9ŸaŒ¿a÷çÙ_p6¬Fÿíôcø²¡[Ò‘ýuþÌs˜¨•?Íò>}Là쇣)®D7ó©¦J”ƒæRªÑTzNÏ{ÄDÚI&²74ûÔV‹§Õ)óˆWçC¹A=¾Œ—f(¬`¸FE'!ö°£™o*˜×põ c%Ìl¤$Ì/æ ñ Aõ¸uÀöHQáÞù»s¶?H=Ï¡V‘JX,‚ƒ…äp]±á™º \yœÕÉ<€ ÇòýÁc²á²}¸rÝÚk¼ä#,;Mâö“ƒ€©l×èû,—F¦#0 –át|ºŽ8©Ì1};t•9‡0é#/ïl2qÝ$…”[‡é²›#,±ÑàV)znn²rÀb"§N#,açÄ:JåEXw™Š}(,ížÕFø©·É×ðq…gÃj¨Q6q=8d‡x5Qc!<Ok‹¿WïöcS%oF÷¶'È»lÏk6ÖWüŸÚ™Ö¿~N'¹!ñÁUÊ›*Ê#0 Ù[_fvÅY$ÐTNPK¬3o=¸Ô‰ ‰Î{Q#0Ø›—õNÇø‡8œ#V`¿=;µQŒÀõ‚*dýri(®Sòª--lm,L»XWW#/òû;„sª*ã°wö³àeÁ}¢Ò˜xãâç>>¸¦†/gµ$UÞtWJüeÌŽ9¼Z£™èåÞ#4šÒêx’€W¼ í.=8óâê‰ÓP0²8%w[z„ôÓý}¸Ô’L7mB½JØxï#/ÆåÏó콸ZdŽš„^@ä¨gCò"¦Á3;W‘½¸Ò0!2#/^6‚)a€ÂJG‘¾ôWß èCÁä¡×Ùæ?Z#0½AùöhÐ؉6I[J­‡‚ø{x8¡ìÔë4Fv”Êæ~sКàf`ðT}nˆQºu'Žvîà©”7£#,ò.Q'´tú¡á#ÇÜl‘`¢—/l$ï»wCnA&Ø ‘>ïœìóù»ûL/\ìÿ‚)BÝ6>Óžß_ù™‹ø­¦[é<weÄ/®œößt lo tòûœø÷ »F—èÚS'”¯O¢ äé‹CFÖ Îh¿Ì“éÛC?/ºÏ¨ùe£&¦d³ž6Þ Ùçÿ'öÙ§gX\hã7‹ÎCwÐåS;ÿ‡¶ÚýlÃ7<][ÓøÅ€n¼†í•/NP¨0€ £—ee5hPl;0:Ô‡5ÇBàLˆnºœ}ˆà…˜oZ ð$i–h1à๡ !`#ÐB)Có»¸À«Ü¼Ù€~?µ#/¢Œ~ØŒŽ\"H±‚Èîé{ƒ¨ ª/hmKÍåÚ˜÷9eê-BƒO1Òpvôg“APÛòüš·Ncdô…<‡òÇ ã(œbØè0µÊ³:SƒþY˜“ñÔj[ŠÑìCÐYV-KÞà\Žì:·ñ#0/@zƒaL!; 3n\- ¾Ÿ?¥öB3ÛoÝl<Zñ±j»çÕH‚6ù˺íç\Cn°¬‘ #/¯Ï…4Ìϱæödš„±Ö÷6Þw¦ùÒçQ]Á wc?ÇQœ¸G3‘ÃZÅà¹J!âØÑ)’úÿ?Ìîå­1ü Tº4G4ò…­¶ˆCiv½_eYňrI%›îÙ¼ ,b±"‚ÃC¶\ûºåË;´½SÏ/",Ñ_Å[zn€u{<ÞôCÏ+²ÖMè|ùûe¡Þ¯¡.@]UúRÁÃÜÓÖ|ÜøœA”#,j}ŒØ¾þ¿mÅàxCvý MÃöùèèïÔ¨HIµI!\ü¯ÀvדÔéƒud-Ÿë·Qþ³³s²Ne–Ú3àˆe[j"¨¢£ccuÄBI^_‰T”Aý#/s¼¦·ö§ö80¬c8i/ãÐÐ,ZÓw“óí\{jnsÖ‚b#/ahÑüúÏÐqÜ­µF“`ÕëÕæ®é_ÒfRßg[Ú¦CRHÌÓâ_Ÿõûñ±ÀîWŸ?#0„¦ƒ´@ºžÑ…?%¸ü ‰º{#/”J¦uÑÏ&­…5ç@ZI¹Êr ¹,ä}/kÔéù:áŠP _TŽþÿÔ˜˜¿8ôÀ>Ã|5Ï»¶ôÊ=usèz`u}ý;'ÝDŸ¦0ûžð[ž&=ßËß³¢^tUÿnãØSY†y ¨ áøü<°÷3wòïó!fª¦ú϶äßÑA„`íì³´© •'îé¡ŒÑLŒ  i*4OQeIÓÚT,Pau,‡}XvcBœuýÖ×û!Ôlj» 9ýž)EšÔù~'ã§_?Ôo‰Y"&ÕÙ¬sZÈË°‘m$#ç´öÎçÁ#/ÁhA Ë#0<6ßtWrVN¯â½ÉcöS2d‰€óy€r+Ž`=¢RëÕÐ\½ƒú+€—¿ÌåF÷¤ËõfQ„¯Ýó°VÓóàQyDJ>è…¡ƒsS7œë¦e²yLï<­ÛÒËF¬2¬'Ü2dA`¤À!Q2Ýa55|Ž„ÌsC‰ÉÒ¡¨QX'B#/L-%LÅ,+*y§ýA6§öÏ‹43R£ÿ@Ìþ!² *kf:ñ£þú±F÷hpˆÍd¤m*¬3XEé–µ#/wb³Ž0\&šµkpoFÌU÷ 8 ;B1ØËPoº ”ȳ‰^¥ +IV#‚ä %…bž˜1ÞÃ%’ħAý²þïA?µ5öÏå±tdÉ0#,‡³+â+5¯%Ÿ"~;#,«ô5G‘Üìð¡&ÿÙ&+ðÏðrȉ†ÌßÙþgtèþ 3÷3—MŸ“i7D|7¾»èsámÕ¸8„AêâH#/#0KE’ÒZEšqjvuì²Ó¹Gcü×Îøx·ÙþÒBNs‚Á÷zx€—ø|ÍüîÃÏ´kj#ø^gÏÜðœ#/Á͹qÀ*Âë´µ¹=´˜É@š^Ed˜¢¢ð9ÍiüØý'Ÿ¡Ø:éòéÞüÄñMïa³™Ä­3jXu‡S©ùÍ}%Ì~³JÏeÝÿÝgì ¥Rð#, œ0¼*5#,¡ÛôÍLjL¦É$”‚°Sðàç$FÕy;‰çñE¯CÞà¢KÕÓXy”2…º÷ëU4Ü;à‰Œ¾¦]YêÙÎ`ð4ÍÉe•€æÉ(3ÜŠ0R<¼j…] ’üŸÎìÁòëæw$!¡Í·þƒ¨¤\²L¹pzï·™MGÍ8ð)4xû:›{AŽ®#Üm55~E÷¦3uœ‚MíªÊ0Ïlÿ3ÃS¡ŸØÛ¯Ô©1}–òZ&¶±ûa61Ã9Ôí¨,áá@!Ë#/ì¥Òù‰¨¯ØOaqrkà{zÆÒáeÈ(tŠ]ÍAzÙ0»'[îæÿb]çi²÷=Ÿç<‘Þk#l¤ÍdƒÝß[øÉì3EÃ5‚C° ë…Ívå­…óNÍúˆ^Nt{ÑRá‚zÝE÷xçÝó;Îs°•ÀB`CÛ »ý?Ù×€øIåæT@Ï…/ã7R“õýVéùõ6?íïª(¿}ΘôBU8ÀÛOã¸w¾wÍö`÷¼¢øG:<?"¯Cê»T‰>ó%Y"±Ÿ°Çm˜èšÛç:G¿åö†þg{©ÎâEm¥Ê¡U—œ5ùtH}׆8²ñ…ÂÇCèf«äã{cm€Ç“°é‚¯ýqa‡ø?é‚çÀy\j¢Žäï‚òÌ6ŽÇàiXÙ1¯F¶‡>ys8€õà8#0#/Ô®ˆƒ[;Æ1Ž¼¸Ü««Úë|#/â:Þ"y‘Æ5—Ñ ]/â[ñOÞßÊBŽûîa1K”MßAG[ø¯“ÛÀZ\¹\±xx…Œ¡Ì–¼4wÇdô‹•ÿ‚ÉCÞ 2áQή¬Ö"øNZp‘mÚšiSål3µÒ–¦Åé UrXz×g8Ä:Ds¾ŸYÚfúïpãÕÛÙe¾ö†;ù»xåï&ï9¬+cM¬)Õ$#/;ëÂ&s‚…( ¢²5§\åºá`”ôF˜òžu.­n™¥1¤Í”ËÙÌx~Æ"ðoû£á“l.äí#,ëM˜à¥ðãhöÅÅöy®ZçE¦Êç…òlÏ‹ÖãPµj½RéAžÙˆµÊ#/ïñáÙ#½wÓË/yWã#/éyòíǤÞN˜»˜†á4.ŽæîÛ,OdS®ä.™Þ:d3µ¶¥å·³ŽŽ'­Óƒ[j¡Éj† @AbŒLÑ Ô¤Û5%E¾Þ¾7éqÛ“Œœ+8eu¨•G.ì·$„ÙAßöÖÝ#0¤[ïܨ%¬†òÒâ·¼ N‹ …’Ïþ3#,H€DvÝßëóõý’÷Y¹F"¡^®¬ÊöðØý•AyyŽ…p—Sµ0‹CúñM+„±¨Õ LALHé|¯“nù·Ôþsl~ëÏ›Y¨ß2FQ¾Ñ?~p¤¨/Û œ²ºab46BYá ×GML„®säX½¹ ëž©÷;ß»©ç9\££³ÏhŒM<JSçjBM·&F!@šÞ@9#>µË̶Û@mgÙ H¯DØ ¢: tµ3l¸]WÎû"#/n_¿Ãô¼“:ò>E(ŒP(Í>F¾– Oú0P>Dùþ¤çê…•£¿otÄZ%êå¶àz÷Ùºǜ‹Ï߯È#FÕÉ!µyX<„CèìÃ2o#ö½7ÁÔÆ|>J;ã#þÈ‚æ€ã™ ¼wcK1æg,’üýÉïf hêÀKFÀïó}µ×6x{v#/Øqé[ëßfŠ’ø9ÇBjQ â§fßÉä9?~ýö콌z"ö71·\MÎq:[Z}TS‡ ñM“^¦~œN†#0à ,$ µnåÓ{fdÆ'9N\KÓu.Δ$í@:hCëÚ c¨ôy¬þäÓ“mu‚]ÃÏv˜¨㇉^6ÁɶoåkÖ%òý‘»]Æ˦yÓÎ%°¬›Râ&ć}7<ìå–Ìó@¤[\L¬MDOŒá«>¬FajÉ*vñãQHŽ4Iz©#Coa¶Ê9%»ˆ”‡AX/ÁË@È6iˆ±–³¼s4®`Í1Âr£!Š„†_~#0>#Ðø±…‹Y‡qæ"’2Æú|ÿíƒæŸoßãÕç?…íÖrc¹P+ÝýßL/±ÿSÊ\êØ¡•ñTçsù¾ïGQÃLÌÉÈ€T>âó¶+í÷[©Ç8²"dÊžªOͯð÷\þ¿Ú>Göÿ?ò>a¸‡÷ °só¥àã|#0òªª°ÿaç`]L øÿ¼acUÛ@oØ붉ĭÛÉ׉ÌÐ6ó?#/»‡ ÍÔ)Ý“úB‹·7¼J#M!%"F#,Daýï$þm Žðã¤Úèu*Ž¼v’Ikp%hlu:Ü¡„Á°Áf³Õb6I.CÑy‡³1G¶I`È´›–òg¾ÂêH4|?Aܪì<“n!P!t·Úÿ°ààÊ÷==ãÈ5YßÝŽÏF(;±\Q£Ì’=ˆ(ÈÄO0£Ìþƒc¼áü#/€½"*¬`ôªVª•F¨¦ÂΤÔ; †Õ;Ͷ6#,Ðc®þl4Û¶„âz¿…?O×]Ä…8%›@Ã-î;gáåbyé3(æö9å#0ýjªæbÃÓØh{ƒ;¶>ç¶xIZž†›RVÌcŠ½Q6AÉØlò!ÛMY¢“U8²GYÕùpwaƒc#/ 1¬1ÄÐŒF"ª4aF Ì4Xpl¾À2ŽV4\ã Ý„}!œB °%–Å^v¾F“Û½Ü@^[@Ñž^ŒÎœU%@åËmg›´…ÀRÜÁ˜˜ŽtÜ6„ï5*Ø¡@'ɳÆýú³}ä…:suqjçZå÷ü÷:[õ„aæÏ¢#@ßïÎî¿5B¼³WÙ°€uñ¨X¡ùD“Îå$›¡UP´2hÉS!‰«¼½×º÷Ï6¼SjK´`D7¬”^׆ë…‚ƒðaõ#0Eî[‰+W÷Ýq#ÊþóúÑø·Ä¥e,Š/äólkeu›º"ŠÊ•D²0~ÒêžÐ¥}#0r4’!WvÝŸD$ï;ƒ#{ûÙ#,ži“CÏžþ‰  M ˜rÿ'a¥¿¶ÃŸÍÓ¡ª¬V"#ˬ’räXõòAl‡¥È±˜ýd68š†u$cm"`ŠÃ•‘Â¥ëÅm«#,=\Rñ.¿º»{™LkÐg#,ÈŽç)wI†n!Æ*x6ý#/‚‡‘È Ú¨ü!^ªÎ0ì÷&ÚN©¬3(Yo']©ª†Oy#00f# ‰?aSäi#/`™PÆ Ç·Çu·{ŸËC×n:0Ü€‰©x$"Pìní·`¹öœ#¶#0·ÕÙ£\#/¾ê/VÛÀò^ÃTD£<ÊÔ_¬ó^EÚë4è*Êɱ?›nãzXUZ÷TÔé„2ÂË&ò.úK!wdZ#,8›ž)s›¹0™ÉIAwxQ"ì»9gƒ”Ò5W¹!Œ¦0’¹&Ã'›ê9àÑÐœ³1ŸL/sÄ;ü"«Ÿ¦+µ\e89dxdŽ<g-)ÔêùëÕë¯G<Žm¼µf¦I6Ï+Þ¸|ÝAÊÜV[:ï31"1Am©ƒâná‘ ³D¦Òônœh]Xm¥XQ#/^ŽÛx<ÃIË56ljØBˆÈÈ7?µ‘Ê™í^ÞPÀY8'PèjƒçC>%×­ÈÕ…„ÎF'Aw!CßüµMöú½ští™Dë¤Å,düp#,IIÑ$4ßÓÖ]úmëàHC]¨XDD˜2D;O©˜± êã±îñѵũwцösoE(¢ ´ï ã4Á“ƒ±r2à .¦3¤$(¯5š¼Èd1œLæ´êl±ŠZìh<@°Þ‚%—¯ŒvlLxxkÎ@ ŽçG=# =¡npHk¼/†‰6Gœpë+Jœ£éˆÔ:°®¦ä¹¾Ê>Z&èõ#,9!Ä>?Aäôù‚Äš¡°ÕæA()Ølt&$tneøºî\ÌÈ´ÀôÛhl`ÀùŒÚÃE„ öIy·˜o:tñ6ri=3ù›×¹øÔØ(Ë·³¶¦àædU{&G4À£NÄ…IYXª…Gc¦âêG `ág¼î&a—¨²tˆ%Jæ‡83+Mª.³@8éû*¢º¨ÜS¥ëpSi6›±&¥ÏÕ¹$¢ÂܸÞMÉ-]#/ÉMBÁÀ„*P‡jzʺJ™¹Áœª§Ülhiı”æ-þ øl¦n|tfca;A¦ïy÷MË—èksJ€ÁJBØšç[Öö´ÌraU[r™iuºDò0PcK€õ/‰#å ÉÈyI#/G¸ (D`«,<ÑÚ3—‘() °5šV8/X÷<'%0åQ©¸zŽ7”Ú!¸_e±¹W;‘2±“4˜‚£IÅ…Z©“™G™ž°å¢êt9ÑÜ`5Ô5ê÷xk2˜Ëb¯3.<ÌÉ—332ÆÙ™Y™3$•c¶g’^Rýú­¶kÕ­¡HXfï±—9b¨jŠ $zHØÚWΈ–#/bÓöùoÒjÊógtv!è˜Éåôe»‹sÛáÐ…¶9OÏ:ª5õÃV0i¡ŒyÌ…Gnñ´OÆχI­xxãK-×EÆÑ9­ÐÐ4Β蟆°kdÌ÷ªeù¢AdL!#/Lá­ôæV·<Ö»5xa=L™;vBP sÜŒ÷¦#/‡´^~‡Þ1Zšm6“#r1YT’UAD¼ :i0÷õ>n7¢×j x±2xp­HÔ¢ÂA‹ˆX³3¤–ľœÒ1­›eŽœHøÝ-6ùžÖyÕS`U,i–Wp†YŒÖU£ˆ˜†Ñ…– hˆ‚óÒu(,Kþ°Qs¤ãª**Ñá^brÚÕGƒãºæ]Ÿì¿i°#/€…IÞê7Fž²ü|£‘i¦ÒCKHÍbîŒ@i#,Pp·”¸BdÑb#0 }µÖ'‰]0’P:Wq,bLt#0á8M‰Eë–棿•Ê¹_›|&´(±Û`æ‡6°ŒtJ›"™½jY²n˜ßmÖo#/—#/ÓrJmÄJlðskDØÒp5‚Ð6“FÁhhëÖÇ6Ç–\¹ ±aïàGºjÆÞáDö›„Üà£J(‹Ðã$á2fdçqªºDÒ{pM¡$em½Ü`Ú½žÌ6¡… ÎÄý;Ï›q¨2„d¢4Øuw&Ppd†Hc’VÚSIÐèªL‹¢ðÎ`¦tGgƒÓƒGYn„e™ @h6m€¼šî×D p­6'ÿ¾7 qRV..Ä8âjƒ 28Ó†A•ˆãŽ‘¨ÊöCý/Ѻd~§&™òº¹¸K¦ûF(ÆÂl‡p Ðå•s ¥îl¿;_#0«UÐ !„&#/ö ¥/6î&{ˆŽ™º&)°àr}ú˜ƒ\`R$ÍÀløˆ„x£*rChƒ$¾êsÆ1dT\³¸e|¤a¬Öɸj)±¾XV9g>#0†#/+d\¤¦«dÆV#¼]™Îo€gÉ7&»Ó6ínR\00Sˆfsö6®÷D¤ÕĸíN=Œñ½'wÆ©ý—PZqÀíÜ—ß“À ‰"È(Á‘€1Š#4&¦¢0A NÒà{NòdÙQÂfÅgA’'‹À1=§:äS_?L–·Tg#g,Ð7‘ê)­í1£«Æfe#/™è,NÊ:Cpo ݈p(UÀ¹QtLŒõ)¸¾îP{X¿> æý(RŸ3hàkÚ˜ ×ãׇK»rWÇÝÄ.\‰¢Š¡ˆ­ÞÞç³Õ³Bº¸†±Ñ®9yV†Ô—‰_ÃÙY&áÄÄÄÌË&a0†#AŒz†s^ë 6n,«$›fimÛq*ç,P~ΔnÈûŒ“?ðhtþ  Pxø¹Å‰ b|~¢g-c±F‘’}Œæœó½.í oä”"¦Ýl\F‹-g‹1›5*P›^ÑÆ^EЂÝûH¢¿åR¾ßü¨ŸKÖïþâ?ã ÑŒ‰¸“¬;1ž·Åq^§«Ó¹?ë>Ô--OÃæã[È›œ0×·³=Pz ¡i({;<6†‡ >üN‚üÜYêÁ†PdBa0$…‹ 6Ce)Õ¨ø !D(Ûþ™¼;eQCP§¨[Y#0 #0°eA >çDpÈ5Ž@¤vìs=?ÈGýHÔ~Vëÿ\ªHš#0Eláöð(@'ÎRˆªŒ“ÛóH#,>Ðp1)#AŒXBŒ¤ße}~]ÃÚ<éI#/~ÿ9Aÿm›‘}g4sæcâtPu)-Ki’©H˜–½î¢##,`ÆDX©#07ÈÔÎhÐ2€Ub]…°‚ƒô{k&)ªÅ±Yø)r<]^~¯—iÝbݵڊœbÎÆHJYç ¯AG}”?Q÷)ý2D»R† #,Òˆ÷kJ€<‡{] wXåaî{ìÕçûÅqÎÒH‰¿¤<Á#0gH¥T5T“óö«,0bØ–"‹h®"˜  ðÌóB(HOº}òå4Ê ©`DÍ]ŠEP7)¼9˜€w"‡¦Ú1€ˆÀaÁPX…ýºÈ d“DË>ƒ=”k$$KZŠ]ÕÈ+m‘EO«vö×Õv¯UˆÈ'+ËØ\šÄÊ ¦)˜ÿÈ£t)£P¸Å` £l:Øy#,€¡DD×i œø %9V°ï‹_6Uuø—ÑH‘š #,=GPK(?m>˜I“žâ C¯q)Ð@2–1ËK+ 1“¼(Õôå]å\îǤP·.<rÓù_0DQÓ ÁÁ< `.{®tQ‹ÄA]Ð* bH£é2Ä0UzáU_Ì[UV-UFµšZ¶â!‰?¢#0#8˜ò~€ÀÏÐ’zmÊÜ- ŒÁQ£÷8ÉG¾ª FÚZ¡d§æ#0ÊÛ\¼R[­@`ÖšF#/`8#,ØÒÈãuô(òT¦£?Ó5ì‹ÅzjRI÷»xõ­Êömì¹s0×wi$¼»¹r¹®?üóo7HçGussIY]Ö9$úçY/9¾u¯KCéR§ûsâ@˜5" p¯ò„b*ÁXÈ0„‘,XCàõ¯h€‘PöÌP¸mý#/À(Ç? ÄDPV=CØù"{1'«Ôÿ£À­²¨¨®xìi¨Q*5‰Ã‰×s`wö´ÅŒ¶’>|Ü/ƒð&ºen@¥ƒ¯"ìe†XŽLõT0’Ï„Zô#0I`ø¼,DÒÙa¯üß"×Á>\&›¿Âž- ,@#,‘S8‰NØJÄPAƒ DŽß“CD™ãÄz}·ä%»#,Î%+ûJ¦GsVk+Ï*ù–ôfjdõwûRMGƒ%“‹Jp!LyW8Â&̆Mupö%$É #”h‰CKhgº•7¹˜ÒÂ’ã`,˜._•Š® HcÕQÚ›ø·Rï ;êÃÕGð\·ê#0­ý–Céþ3žÝ)è§è8Bk\†S\ý>fÖêOa÷xÄüÁa·q°‡ÛV«k¾öÁ .€½Á~²u輪k…ù.Ç5~ˆ¢{ß¼íÏ—ÇIµ´©åáv‡ëŽB¹2Ûˆ:Ÿö!ݶlj¸|ñ©ò¯-·É‚Ö gPgóÌÄ°í³’Ìý)ßh6Äæ²¢úOÂ#0ü}?*! U*¥}¼÷'3½e(&ÄóÒgKâÜQçAA !˜£ì’ ô 3!dE/FO˜ê©³L:²‹ªUâ[#,:mÐQŒ…†ÈVŸU•"ƒî¿ž}š\»LÍÆÎV,Aw†ìwVÐîÏáí:ÊþøÙeT¨ÂF¨¦I]ð&àïêJ¢Ÿ„nÛ›V-lÚ4ik%J$¶+›JÕ˜Ólš•±Tš¬4¨Ð¦f­HTˆ*yNèœmò•'(pÔWÀMñRDD‚##"$ˆ/G´dFȾXR#/€@&g{üûA^…¿†®,v¸dRƒªÇUÌ2`<42ÅQÎ$ÌŒhXBÓ1¸?¦ Ú#0²ê†ñT1X“XÈÔá¤7×\øD1í­ùñH«DCX%ìbÙ¢mâi´sw†<Xnhi(È°QùÕ†dŘd)J»*Ž¢®šP+Î#,è#0îŽÍ2Œ?Ñ}ÇÖH4·Å­ƒuÅHòŒ1`½2›zïpáÕàð/€ç( ¢T¦Ð]6î¦2„ !–Ð ï§Q_µc›5,¶üsTi)š­Í¹²W-vi›e¤ÁQQE´VåÄ6ÆÞÜ¡ñ•|d‰ág ”7Æ2"°$‚È9*=¢  …´àùt­¯ û=¸[…4ѶZC „M}è¯GF…„ô=CyŽá:Äí»SóÁÚHx|!€ä{ä=wwü~ÿ«÷þ¿›ý_ÛÇ‹Ì“æ{qB! ð!gw™~#/ç½{.iÜÀ«–½jü-Ó[_“éB0cñ4B!E0*!…_5S™ç6á{î;|ùXÅ\ÜšD´#,Ö”]`?”šýyYE>–/° sŒ¬WñÀè›ð Im»¹«¸ta•Hw¥øÿU§ou{oŸaý å$üÔï`i7V ¨eý½Ã_•¡hzùåáÃ1ÈðNÆÝä)Ì1#/·„—ƒ…8/£ ÃPÔ÷X ’§Þ¶IÉf@iœ3Ǥá5Ð6@D@hÒ:#4›z^IÒ|:ŽŽú;Ž»ŠXŽs€†(Ǹ®Òv•a½™ß©c÷éA”ýòe€;\F^þ§G§Ò{7¿„'xC±©ß›sƒê¡ßl(÷Ý°ý=‡X(iòpø¼8Vç«ì˪¢ðxì–¤aòwȇ°øÓ§„ÉXæÞ]ú9eåÏÙ]ôâCcØôØòÓ07Ý¿Ì—*üÿÍ'"¾à#,ÿ\Auëê’0$¦€{-¸PÀ1f ï;µ*Œ¦Jgâ”.(O¶W7¬9õdÖö' -¾Chy•¦©8U*“Ñ ÈgÅtÝÙŒâê˜!Ÿ#"# iä=Ò&8·ÄÆР⢶î±aýHxa#,`ö•Hø‡à ÚûA\òCÝJy !¯O#¢²·Ë÷þÿw¤¹W”þkáà2 g•TT*츈7D«ª>®‡ˆüÅ"0_8UQ*„€°F#0,ïù¿ŽPÌå„5*+Ç$ ÕÔž~]yC#0qÊ‚Äol­«ö$<òAV1óš¾–•€Ø8xÈ‹{wپ̅ƒqs›#,÷s0äœ(EbóYʈ@Ï8|ì™:?¯Ÿ¢»c/RüL¤ Æ̓ìÅ¢ šLÛ®~ãÔ¡ÕÁá™Câ*C¶×®Ë Jê:•ô†^œ1º´Æ)a¸\·+Çd¿®ü|›¶“{Vç2= ne>|ÏEã|sæðNQ‡›a…6³U()8¬jÅs¢#0Û´síx˜v$U¥ƒˆ;Ö°:<=v¶ã§jìºxã¬v×<®}ä¾ÙäÇBËÝöiu¶J„Ø@—(íÃíŸç­t ãyéµ¥G^=ݦ,ì»(É»Yé}E׿<{c*[#/Ï8æHˆ9x–ÁÅôÜëg^M³µ\9æ>;ú»‚­›ý1lj­ß>ø#i"0 cµnÊ6–8ê8é0Ò§j-C4ÂH#Ô(¼X¬ÌÙ¡·ŒVÍMÒê<ÊÄ0(ô4|>œØué¼úî1TFÞM˱48X5í­Mºvô\˳$7w4å9xQB#Û×€ž‚xü_J&>sà|}iSñsØp—RˆÞ˜U!QJWç¸]h~}§>dÚªDp3G€¥_¡w?pÄÅ ;Æ—€/s €#8ë#/}×ïdd°{]Yáp0áàBB×#/œ0]¶|¬‡çNˆž‹æ9Ýþ7236›pî7Ëä“>úñ® Z._>6ÛÑÝIÖa@_”"ÍÞ~!€šãÖg~ÇÏí|ÄÈ67n½ Í€ô½¢ƒnlCr#T™,¶À¹ód½™òKN6C†¼+ÑÁVøG=t¹P¹¢tðä9®ÓSîuì  Ëcžç!QÄwg#04p‘ׂ-UJ ŒF½|N[b ŒK3E/ÆïÂêÕ(䲩TõÖ'·ÙòÔô”PŽýšXp0 @joÚÝÈêÃá\!ƒ ±L‰Ž1‚¯Ò[!då×®Lf›ùõÏyŽ›45ÀûX—avxN¿6œy#/…#0‚‰ýÝ €­zªK”,^çñ`ÀèÔŒÈDƒÇ*¥huI!õ4{ꦤoÙåìÈó3Ô'#¦ût¾K¨ý8˜õN– /Ù›¶Ë'z)š¬©©Å?w§]ªÓåÃWVåç•D‚ùƒª^ÌZÙ¹@Ñ9Ýß@󢱘7¦ú¼¯/;«M6¦¶º×1K#0²ZºmÙ;«©f‰£m#/ ÂÆ)„„YA ®XŠÆÔ,U§l¨!¡J"(†—SËܱbDp\FÊͅ÷\pº]Rá"°H\&Ó8&yAH+$PcÀ‘$¯á;ÞŸyr(Ú›9u#à±dÏ(7]¢Ä軑åmJÛ¦Ť‚„RH"ü]t¤Y€m%ùUÓfMfKäBÉ„$žûÏu•@D¤…ptœ tß?ÝGÊﹺdê®ìÈ©Y–ËÉŠQžTê °¤“vÖ Ó%šy¤ÑíJ·vÚÑH§„ƒ#0×`„ã©jÛ Ão©E=_ªtÂ8ë¶ûÖ½¨#/4»v ™Al¯Ù…\ éÚyD´Åìªý.ÃÏ—=8í<v‰ÜÁ9!>Xò;å|Ôè« wJÀmeP#0aäJ"ˆR"±”ŒŠ#NFâȆ"ZÉHAl¥ –…Ÿv’¹Ð¥&`ž:Ÿ*³Æ`…ð+]{_†û¦V4[ºíVæ«•¢†!ò(¶l2—SFàaiõäÓÙÄs^\òHLwð:󯪰´½)³ä³ö·U^,›-ºâÛ=‚È„…ôx2 Zí›p$µßžAæ‰ÝnL´´Çiª.5mõžá¸i#/ÁŒ$ž{vºk9äòÿ=dËáâYt¼îáØ­´NÂ0ª¨5! ±JXòˆó Ù"â]-dÙHÅÌ3B¤q Üɹ¡Vù‚—‰øWâÔÌ1­“ °ªd…ϳ»áçð:ûã#0’Ý°ÛTzO—ocÏeW˜#/ý;ä£kz(W#/¾|r6I4M¬XŠ ;„E“™ç„éUçkWÊåÕS9¦…ÊßN<ÅQ\t˾Ó.ù2pIä4á‡Ì&÷F>Ãs)Ë…É$œå­Rצãßgµ9zVÜNBÝФƒ Cîn(Þ»o=_I¶Þ@ûyœ»—8ƒw”$}ù+HØ_šk`#/N`œ;>8’P¸sáÏ|¦±d7ýºõÖ:ÃC°ÈrÁÏÕA aUX¾aôëêi®Ú¼#,ã]‘kpÐBm.Á²‹Ç‰!hl.¡#`×ç• bHÂbæiÃÀ1 AH#”8"#s¹,Ô#/FAè@P€×EiЗ9xT Y"q››Ý”¯ÙŸ³<Ò ‰ª±#üØÁ#,ÂLÝB¥E@¨ŠÔ]%d€¦Àx0ZÔ à2Ѧ ÆØ‘`ŒBˆFµ€W#/#/0dL0Pqg™¥­ÂyD»°¼pÆHRÒ_BAãipÖé}<¼ÞÓouku#I¨†RE`MÊ#0$…YF£ ]JXaé²Ý„6ýÛ—·'¥¯='ªpJûiLx2HSÜɸœ#/ab:›õ|`*1qîQ|™FÝœlTM`_KXÓ7ÐlŠéc#,Ú§¬(¢äá«Ÿ)ô1AŸIäÃ&¡IH£:m°{û:àG©Áø’_¼–²_¸4q×Y7?9¤9#,³É% ߊÁ²«¡OÓêÓ0°Éf9ÛÙááêíGÒ. ßÉàfiÁ,œ/á]6öt¬DQ§.¦Ä©;–?gO9ƒ¤x‡#00Zoªë´ ºµºßyùÿ%þ–ƒlT˜:)Òªt©Zžï/§c¾ûCvg̵ëRÇ®tO¯LYßé6àO#0=}3r¥z "riqJ\Tc" ú b“­ì½ëó¯‚óÛ™ÌV£Îÿ¾'Ɔ’S Õ…Â1$1#/Œ2Æ#0'à”ªEaŠ¨f8bÂí]eÆÅP*˜Æ†$´Q€Q¶)zh†-Õ.»ûìõ2…ÅbT«aÙ€—ˆg˜#±#½àsvs:ù7‹ÌbÕ ªö«xæV‡‚ÄnÚ}#0­[U¡1à#¾† P®wzAú#/uƒ›ôÅÃA#,p&9éc`º`Äfaú#/Àn#0†è¾³#,#,ÂwýmUM…&º¨#,â<&;.«V›T³ s=λ&¾Ë™¸³¾Ž§-²n±Þ¾‘ÈÚØm¬£^2Y–d˜•ûòmŒUÈ’ ¼1fsä€îô¾ŸÂû}õ >)š(ô·­èˆÿ$<ñS<#0îˆeß{½Ï+1ª 4Í]6Ú²‘6;#9#09V#/6)¤þ›1ôj2¢‘RAÒ‡ÛÛg†qÁÃ7C¦z5¶¼µ”® ¯>¥ f¢Q•z‘2t¡,j´¿s#$ˆ7j0bhJ,„@YPb`¤1šÌo@¾Ì}Ó‚€late<€µªT=êÆƘ,GwR6º9bKÍ¢WÖkŽGž+øï4ÖîvtËu b¸S Õ õݸ%…nFÏUO­Ð”¢V¬Î|³–Û÷6Û\¿JyR Ußb —Ž7‰Ê:çlRëÈãBBU%;ÊÏ t.š8þ–¿¥®C8„ñóèˆäÚņ˜†ÍhKÅ,Wq6Øi‰ H¢S‰Q"!pª¡£}.ËÄ´ß,¸JÊ9ðQA¤FÀo€œ1l®¾¦”1%G¹‘M±2%Ë;I²•œ[Âh“t5wB ŒEÀÁrÅFЄS@û0ïFÊHfÍí±Ì.šäq€ô2&HD3c %¡‘#0a,VMPË{Ô¨Å"Ã&Éá`T²ÈfWøÁòMåµC?äsƒ|LÁ™iG+)¡$^·ÌfÈÖÙiW¶ ™Â¨'0ò‚1­t3»ñäwѦsÕ˜%Ò¹üèU80<B’`åt$%u“Ž…Ê"U³˜–c™@ƒÁµõºf4Øq¹ÌeVõ®7óåAÉf‰‘‰Ab#,Ƚ0M„Š;|(#,×¼¥54("qïJM=4)ƶˌÚy™ÍÿGÇeAÕÆ>©†ÿŸ2ùĺVõ2ÐL¢*[¼0 vNéxXß²uÝ]0’m·ÇR«cÕs³d†Mé×)†—Ÿ*Ïq–£/Ê‘pÔÏ—“ï|8ìl £à~dæ´O/±ìß–Ú¥nem[£RaÝÁÃL¶ÈÀ|™í#±¾IéZ‚\‹‰,#/G^À› |¡ïGñ^‚‹Ð†:þ|cÌW‡>>v °#0@Ïî5÷ùúIÚ–¦–Ù°±ÖÑC .”]°õ‘`ç‚Г÷{)d^õ€ :,ÃK*ŒI¨’&&›:d’9€•„LŠ­€Œ\lb±¾Å°ir€4#,ÞxsL:mDX5À‚4áA„#,„ h›ƒLTS#/Û97Gé"Ð^l#0Ïã’4‡¯ ýoE‚¨’tÿ{,ÆoÆîËù¡ÚŠ#, ¦Úž:•í¦×•žhþ<›¦{“Ùª”ÞKÃy{)‡9#sÑ&1¦ú¿¥AØÓ„ß¡€¾qmcQÓ?i¢š—•‹sšÏ“©þ¿\Añd‚0Áè|¬€ó%²§œ„>ÖEQ@H,–ýUÏÜI`m¢ÕFµâ¤V¢²H‚j(È¢’Ñ…#/©ñÐ{<ݦs"!†Ä](oDð_IÎô#,6¥” T‡ÖÈ×ÍØw·:šžãé:Á<EŒT#0ˆ)“)fÔÍ2JdÓ›bÒeAûN¤ÌÕ¤0bÓJ”2SbP¤Ö˜}½¹C&´˜²J%3k4)“)™#2#04"¨¦%)aöwQÆÈ¡H’)&K(#/Q™0Z*Th°E0Š3ɃZb›bÈL¢h2f`”¨ÌA¥ ˜¢¦ŠFùù}ûîUvvìwR#â›hºÈ=ÏÆb'¡YG_¯óí¡°Ÿ¬¨‡‘¼!hÕ#P¨ä_‘ÃÊb«"Ã3Ò£§,d0u8’[@–õï86¦,PÑé,?Ï(`"°ßS·Àà{л͢ˆH5÷Ðy?}MÛ™» …­ÜnWÅÈÁg-Ú½w5ÎÐ’¬L ZS÷K^†^¸¡9õÌÆÌŸ´–.xø…öܼ8iÐmm†8`a[XÇH’Xl#/þHUîÛð×ê,LjQ¤†ÊÉX²QŠM¦Y€HÉhs0w¼¨·…R÷paD1šÊi÷4ãŸY‡23áâ«,><ÅpŒRS$¯–9m5RE2-œ»õÀ¿Ó\6·Ê^ÇH¢òz>"‘þÇÚCº‘–¹'§ièêO¡Þ¿‰!ã<Ùì9† —#0¤:j‹ ®¾GpbÐg+Ïð³rê›Ó.¼Ì©¹jK˜»q>-ö#×™²ˆBNË#/ lªF©H´H#0’2$æ0êÞ>E#,”ç 6ª½nʵUyu¹ÕÏ9Ä8…›RuŠ®ïÇDÄb;Æå·ßÍçl;ôw‡b¥–w³ãÏùØ„ ’1,-“P½QA#,îñ®ÛI½¨~öA‡³ËÆžMx¸3ŸTöE7>sË3ì8qÒó\^ßfžúméÇ¥×™r5†7äßiƒ¯K Óì˜sÄxßz9Á#/¹ý—±¬È°](%Ñe ã-tÖÙáYÖ²ƒ×+S*,¦å¤žOA¡7MDSŠ ‹Ìy%Ñ ð’mÄÚÂðk©FSW/ÊT§©òß5É{áÄé(Vôã„&Wá ‘•¤”?÷B`ì#/ó'#0ª.ÐÊx JOKdƒmw±Ñve_Z:o mȵ¡ðÛIj<ŽÃ¥³#,OÏé›ì ~žšO6Þ}ÍÙÝЯA­$P!ËwÐN²­óÑÐOMz;áÕ×cðô–0Ngq½ÿAÎÒt¤*He‡ݽCîòÈC¨ìÓëçñÞe©ÛOÏð ²´÷(–#bP¡Ø'-¸ÉæË©Ã/Ì´ñ*OoW…½ h•K/AQ%5˜zSCoá²ÚàUÙ¢¤¯£RÇh ¸AQÍÙÈÆÎ\6&D#/3ÉIÌŽ×q݃Ý<|‰ßPÌt0µv“—³Nˆ®€€Á °Jì ,ÀNeûÐdF6Ö½gÀwéPIZ¯[3#/å‹,C‡&™ò}Ñ‘Š<kR8df`Û3R°Óƒæ­Xåy$cz™šÔiû²Õ½Wô²<3l»áÌÌr^uqŠ¹¸e†ÙÈØíx,à âhÑ©¦U…—‡3KYªV¡¥¬30¶™:ä™…6µ3LÐiì»Íé«#0°ãôL͹ uämánò˜Žƒ†oLuó‚ý·]¸#,aÇݦù Á31š£óu¾!Z”ä‰bȺÖbèMlÌÆ´lÇX#/¬kMRÙëÔ¯ÃÁ»¼#1²ÞC´ô}N©²·EN Â/N8¸qÜw—©x3´aq±£Õí`‘™ÆÊ£LÀ±i¡'"êÁsIÊ–²Íuèb}î©Ñ~ÑdäL(ìeIo0@˜©~¢+CÖ†'wgm?f™7†ÕØÈ:óm¦¬Ü,èš«}FÊrF­Ô”Á:by7fë²4ˆ¸XêÌ…~¡¦&#/5ríægcªíµxÉ:ß\Y—Ó¨#O1ñÆvÆøƒ¸uÛfJ3­FñœäÒÇÏiùÌRa‡Yir}Ü›ÜÓñßn¼nÉøõʑ°v–vˆôëQ¥«¬Ž°X›e£u)ÔáSEdÉ6“QXÄ9çþ±Ú¹½o¨;íD<Æç:q^q8‰2Ëœíƒ/dRÖ/:¨ÖLâñŠêù©ßU°¤’g·f…‡m¶ÂªÜßlÞøŠ=5œg-÷ Ûbä‘Åž·’tõx’Ö4äNѨÇ7ÒY,mxHˆYëÄÇVÃeØâª*…?•m‡ÆøyG:»oX˜wy·É:}‡¸4ÛÌbŒÇ}Üm³»b4Pûb+ŠxzÝž#/Õ—¥)T³Êdl„²MLc•£U4™¡I„„ÖI7;-s•‘â vÀnØÅ´…3:fg¤#»B ÖR®–#/iDÎ#L‚Z×I’%ëô‚]XoŠ¨0(ô4ˆÕG-CšnÇâ †]Qå’zs¬;:ÌÎ|iHÈVôø‚Ý5qW‚°rûE옴Øs(Hd„„˳«‚#/™ê7ãÉáq¨w åâî>‰å—INuª48ßxuÁ¥æi[ÊIwcAyÊÇDÉàÍ&æ`¬½F:ÈU ‘ÂãyÞëº)’ånmellðqƒ(•Ã¸„rŽS¹Å?³NK!ÉMyêmTs†@S³Z!°Ï$>ÎЈȒ\ŽØ7è$h'ŒÊuÖ#/P\Ü@;ÂaÙ#0›zµw6U;"áÖº< ؼ‡e½åQxáÎaèHâfX'0û9LC£Zyè›$ÃÁµ;b˜ZsD†Ï¦—†Æ‡—®Z9ËÖr›bå©4Ê#0‹€É#¤¤¸êéƒÈ.¼ÁÔ×ja5_r›ãlÃ%tÜÌ,&–2#mÛíVî™ âÕQ#3›"@ìHn¯Tc‡{síT\À®#0œ#0ÖN%ãð§ŠÐã1M:I"æRЪÏÕoE2¸v¼nSÞàœ«uh”’xœMÑ›DUufîýzVK¬ÞW}J‹“(JÆÍX\'s¨ºÃ‹62ï,Kݸ͖j¡…`šåØt¥/K»#0µP¬«s&\Øõªcp}4vGC÷™5%oZo4crÙY©iW7¯«ãÏ\ï8w+ŽJ‚\¸y‚fTÄ[Õàã[>ƒ4RÂA欭E'}ªò|EáÑN׆*0¯Ê.i NÓÒŠnéKjh–ÅP0 ÄÜ*µMY©€Ë-M¸Ü·Ì´+£ÑIÃ$8ã57V#o‡§ŽRL©²±³8º×CX÷§#-š»Ê马ycÉUc2ÄòL ÝZ™­×]í–(Û2pÊ-4ÙYa» ¹òº¹³áTqÁz¬êÕoz‡Š6C\¸õŸ\ÄkÄ›­b³½,¥šŒ)‡ÙžÇ/˜"ÖnL“ÆJ&ŸH0ç{–LŽ›Qo’D¶WÄPgGÈÌF'êD^eKÔÀ䩲GB)À1A&¦jr°4ƒ´6Õi.§8Tqª2ÉΦ-R¾Ö¥Œ ÙÑJ÷Œy^3xôÆÏVrkOƒ'²qý,]—Xtm¹ËkI&Wãp«Ñ$Ò±6*ä:‹wœÅÕ#0’%;“¢æÍ6Q}†Nhµ”oº”¯KÇÇ#,ÒÛnºCG3G v^†’ØË!sYNM#0Ó#/°t·E0›$!²ø` ¸àÙHŽ´lbñ8c‡²!‹¸±NÝqß-’k?‚ŠÈ¬=¾šu ; D“Ðÿl-¾”R(E&1RšIYÕ…b£3ˆ=˜‰i‡ ^¿804z0§ƒÁãL™`¹6 ¶H°ÜiТÉHmT°PÇZË0`aBËk5lÙ…<šHŒ2Ì lWìÝ%²Q]¤¤‹5ÅM“ÍÈ™ÉA9¥ïStÂCF^•)#/Ð2É¢KG+„ÙLÊ]·ÀO–õÉ};?#ƒcS+‘úÁ…Ö^ºàRðYR-1¤ “4i98&@]L Î63‡¨Ï<˔ͮlpqAe2L ^žÄs´‹;‡<ðû¤dË펄ü¡á÷]Î’ÜJãMηieÆXÌë¡Œ¾Ï|ó[!ÍŽrÒ±-ÅÈ}#/Ô6ðChÈúM6éñ:²a%f” ã}¨Ùøïµ¾»éÊÈw°Ø.¤² °<+ 4ÔbeÁ Læêi£0ÙÖ3Œgq©Ç@Xzòa!‰­¸Å”iµ¶V º ʨªß±ê¹)Êd.ƒÑE³†V0…“–še6€¥Áâ3ŒñõÉPºvHŒÅ·¾ŠÆø\Ñ\«Æ«ÙµsFÞ¶ÁÆ‹`ØughƒÉŠµßA;O —c‘’T48Ô4m ¬ˆ¦IÃéögX[.UŽ±¥¬—üÅÁõi¤ÇÈÀ©ùo3 ©Œvî­¸D z$b_xá³ËÁ´8Ž¨ ¡↨lá-ˆ€Q™Ã6`Š¡Ç}äY2C¨Åf&—¬Gcy#,ÕV%ébí [Ê¥"!À74o¸Âc0“Âêor ”:î):C]MŠa|‘‡š(‚\¸'i©P±BàC'7#}p× %Æ‘)iN‚™PLÂP]¹SD ²e¸Ã%½ C0é#/ „(à›™›pÁ­T¡k@¹ ,¬×›z‹“h*ì°á6@ȸìÃ\ÁÌn`èÂÁ¢R9ˆ•‰c}í,OQÞÆa¶jCjpàÜÈ`4™‰v[HÎp¡RX)Ü:¡À¨>n쩵“1“5fª¿›-v´~­¥/úI"B¶"†Èh&öZ"mÔ¥0ŠzÒ ƒ_ëÙþ#,rWìñ{ˆí_e~M·êÝ®Š±‘ZX2¨q€b0IÖª ë€0¡3l–¥+õÑ#0’ *gP4MŒi1­æ€#/$°  .ÀH;dA~¿OR›C»:~¸X&|©Û*†XgÒƒãÎñ9l‘ñí81ü‘dsæŽVÅ>›¶¢DÐLÍio¸} ¶ Iõqxxn+¬F·²3qRÓ͹™0 ]&MfÒ´##0Öí³`‚Ù°@Àq³r‘ZœC^'`bÌ·«7B,èìÆúõ#,c=¤å±Á?ÔNéj^ƒ|&).P¤IŒw©Äÿv´ˆ.ÞÛ·N4v¹fk_&µ¤Þ@„B*ŒB†À¼<Ñ$€`°tùY zºS.ñyæPg@•¨-#0£LÐ4M(–™ù~_:$mpý< 7CÇêúB#0+·îÚ$H_Á£³+I•²’HˆšlD$M.nº\Xn—ö+ÍOê©îE­œ#0Þl<ð;TO7Pò'Æâaþ˜d7I)›AS€ ½€ñ?F¢ô#,Â1Žtƒ˜ûˆ¼xõXdâE«PQ€Ž_qì¤Z»J2ô–4?.2b]üê2.)þ™¹ÉúÛ ìÄÛýçÃàÃÃÓ‘ˆc=8dcœr¬›ôØ&šTšm乶/;µßSÏ4m®þY4º„²…ä|r ÍÁiM_fÁ®Xâ8Rh5S† £…NðlxkI›Ž)…ÊÒ“:E̸ÝLðýÓËfIE­ %Ø4w‘Ž@ÈE6Ó_/ˆX©jÚ –FòK™Aì£çª(„ZÆØ£Q¶¨ÖÆ0›¦eIPhÚÅ„¯¯è‚H…(ÜxÞ'Å7û O6•¢§ÓÜ(%ºÔ÷mëð\SÅx—GaÝvÓgx'´‰% ¾è±n6ˆ,–"$Dm0Á%à ’j;Ht¶Á‹p#/³#,ÉÀ<'‰ÙÇG@‚@‰ØôÑi<]¤þk²x˜gƒ´ æNiŠ²™C'éº-(©÷hU“#0ý#,î×xˆZN^<œ«!Y†IÓUÍA¬™d¸8ó©AoöœT€Æ,u¡Ežâqžµ«•t­õ­ì½[ÑfVñ¢ì˜Å³)K 6 i€WV— ÍŒXÀdsùÎ2‘5Ä´AX‡9½ð‹öÎŽÑIbi¡ã‚mÉ$ ÛIƒ#,Ÿ8eëk#/d{†f>Òz½·Æ)C<L‚ov¨·š^Bg›Ñ&Fݸâp6—.ÐÛ Ôq”/yJtÔÞŠV»:6Ó”@R'qpŠÔ^&Ž²BBüÀšÚÍ@8cÉœEÆ.5•"cH1¤6 cq‚éɈ®Í/Ó<~pr@<SÂ'iÝâSr1‰&Æ2°%"L)ˆ†Í0ÇÐI‚â]iü"ÚÈ!PXKÀáglrAƒLÎ),1ÇÛ#,3^^UV©iãås3\ë—SœÙªg 8‘µŒ¤ýr…ƒ2•÷T#/¦­@Óz›\ÍgSRÖ”Â5ÇÒ‡ÖóŸyJ6´a(:u}KÌí6ˆâðÊˆïŸ Jk#/Ùh°Ði‹7joEK… ¤ke1`ÖÑm-,í!-ŸëØ1aàsÓ‚%u^‰gUŒ÷8yׂ–œÙ`æ³2ÑX–1$¨V ±Z¹ª$§‚»º#ÌX+tûš²Ðea1„F?Ÿ)ÃÁúºÙœ¦7ì`‡wgFÏb †° À呇‘8ix7œ01•‚œÃb"3‘2´r²äMªžæ#0íK7†õÎwW#/pƒ4 ÏñqÖ=‘5:ÚmuË;Vôƒ]aÀb¶t‚%—.F0(£ݺº°Ä2±Ì°Ð¸#0,J‰UA*$›iA±€š€ÇHp…ˆ€@U,$˜;Ʀœne '¼Ù|ÐÄVú‚Þ(yöð#0—p{þ”ú¿ø³&}Êå4â‘E‰ì `õà­ÒƒÀøJÃæ?ìñIúaïTAÂZ»ìžw°\1£»@zdCï‰V«ÎzSBQªy‹ÇìdŠ&w]ÝÜ»–í÷ö¼WóÕQkFÖJ«­&­Wæ¶T´i%ù÷äÃí0}0|ö´ñOtçÅab\½ðŸTT¡ëí?¢;ê—ÙÅñ;âY,,Õ¯ñƒo4Nº§xê´ok™Laʱ+‘CdÞΚpÖw™Ù4Ì™W52:2¥Kºye¬Êu…X¦Êl!²‚‡ÌÄ#0¥«; ŠéJ"hd6Ä0Y¬a Âò—³ÎfÜšááH Š9-Ö<Ì8à q©„¡•¨zyv§‚·étµÉ'ÒegypÔ›"&æK¡Àt)¢øš€v"ÞÄ£h†P–ŒJ†±W#/g¥sùÞi¶?z"#0ÞM©Ö⼉“¹‹x²Ónœ¤R–ŠSæ)#/ô3¡˜é:âLJ¸vFµPó:}¢Þ*¨i½ðxXÛz›Mp“\ÎJùš}2”æ¾Ò÷dÆtÙq’L•„3ä]s¸Ð&QI E ‡/J¥òtl›PÖÔ9¢ó3÷0°| P°]‚Œ#"h;‹Ûi©õ÷Ôó‹µcUvVU-Ê»EŒDqX-–QëËØiZe¥ˆ²-%!Ђ¸–…!q“]†V¼¼è£]n[iÖí¬ÓKkšœ’èR¥*8ÅŒ#,öÃ¥Yº’Ã#/óþš¨çÞ†˜eP1šÕ}1ò—uÛ[´.uŽÍÓlwuz“su2î:ë®'N¼¢kt¦›!·Z»I¬•Ývîâ“E¯;¥-;Ow]æZñR¸Í·+vš£H³)ÿ ñR$ppRŠm5 `4’·Œ¦´Ëd”™eclZš‘L×Sk¥ie-2kJeFmTiö__žQ«3A€Ñ³6µ¡*+h‘$„Þ[×ädò5p¶„Z= ‡S☗ IˆvXh"ð#,/yÁ´`¦ J"Rª×Tyf4§/š ¼ˆyÎã×`ôe„ôŒ;^½?C#0XwrØe;t#/#/}†ïža»£h'¼ˆàî;ø½Ñ~\?aWêG/ñìÞz†Ú=šòÒÆÀÔÃà]#,"Fƒo“2TÔ±‡›U¦ÚqÍ1Tïnÿ3[œÙaÀqoRja@œGë [Á—¤I¯å‰Z1! ÂåÓxÄ32¤*!h$Xpàr£ñsXïÈ©#/X™»3=ÚX·×“™ÆÚ~0#/šD°*V9Sdvf$¸Tƒã\-je2+á¹@À@ŒWM›ý_¹×–Nn¬;Hð#/MØp2Ð>uz.<ºtu?¹“˜ü¤òÁ½öà¼,Ñz"å#,@¬\q¼'T?w…<EÖP-ñDì€!“#,M˜¥®^–¨ù9†Ä%Ú°g¸>Å šŽ ‚낲œ Øk1!#/¥Š¯î±€U°Üñ5·Ól-Oºª§ q)r œšoîä¯ë#,€H€@ˆ/‡£äBºaõs67ˆ› Tîµ”~Q€ôã:”pùÿÐÇçab¦…•X@/Ï6Ö¼KÞ®›cj""‹hÓ4J“ÞÝTD’Mã[\Ðf¢VIŠÞn«\šm6‰Ki6b©4Ë&šd¥c1K5 Leø›¬3jÊd6jbKEj–Õ4^÷m-²»ºZ„ûÕ»kµ|vìQS‚fUi’ZšV¤«jhÅM·ñ–Ûªû…F’l‚¬lšÖÛ$‘6Ëm¯•Ú–ªI©kyÞ¤–›M”fTÕ†Õ–Ê߶Û-±ˆÑ-Ñ´Ùo:ºó¹%-S+•Õš¤Þ ¬·uFLKm ­xMqŠÍµ¦ÿB$+Ø¥á±Éò›v†4ɺsÕCþó&¨מ§Ûôž-{wðáЊñ7ªÈVl+†¶I„6é×ëÝâ5±ùýÞΪµX™¤ê¡=)-rœä¸rÅFÊhÍ­o›m>{¦jZZYš[JA€p²éõÑ$’h’"¡Y¥l¥[á¶Û¦­~fÓKÍÚå5so³º×”ªéQTȵDdAJß@­„€š‡J¢gMtÚP¢b¤E4#QdJ´¶j6b–32Z¾ëJèÂQ©IjSmZm”ËY6°Òš)R›}kr†ŒI-F³lKA1J4¥£Ci¶RšDÔ™¢Êeš-ˆÂŒ­”RÆÉ‘,#06J²UJV#0Š”ÙRQ*“j)6ÈbÉeTjͦ•!BJLX)%&L“L”RÍTÛÕU‘#j)bel˜E•­K5“M)’Ò–Ùf´Šd€•B Š$Rbˆi-ô•ªé³fµJU´–Ú¢ÕòÕæÍQmæj±khªRØ/…UÍmH•jTÛD@ÐVh»²p^wõ ˆuœóx³ 8¶½°†ZäïÙ2í›0ñÐ#,CBTîØ?Ë8ã(š9ʲHˆž#/ÛBÏi%Iè<Á¡åâÒœ \àyvTµD‡Ë+÷ˆ¬Çh(û¢yþÜ0D7ÇXõBC îÐÐ(ºŒWçååá»6¶íÃÍ«k(&cÒ]ÛM —C’”nÒ‡ƒÈ"¥+´Äº¯’Z÷/.Üÿ+€Ñ‡¿7×½™C¥#0ý¦ÃE‘@Y q¡„†ò<€§1Ìdq¡1‘5,Ubª3C> Ñí#,õÈ×ò°O$ tPXX äl5ÙºŽw­Cl«<y5È?VJØ¢)oÏàP‚TñÏT8G~D0 ‹Œ¥–VŸ÷“ï~fÅ)Š˜=Q(¡ÙÇÍ–7mòCÏJ°•C|v&™x‡Xœƒ^[±Û}Pîðsø/qX‡:>ù)grãX°þ/—<¹{ô.×B¤Ì0(¾Š‡ÉRÀ`ûèÑ-iúå[¨c¥ öíg#/‹ ¶ðJÎpnä)'›QE«Æ䇖ü#/±ê¸7Tq4ošÍ`™!³#/”TІ†eíKçw”#av‘ëX…üŽ—Öù÷mñÚpb¤]À#,±¶ÆªŒ)'#0¢€¤WJå œÉÐMÚTAöƒHÐý¾¨.¡æu´ž¹=Y)*`" ñ}9ô>h " dØñ)G³Q¦Ý!p: ëèa®Ñ=ï»éHÙmzV§Ðfoƒ ”üz5Ûí#,òÌ8—Mþ/wbèpê󺯭ÀóWW|6Ä0:Ìoì#/¢ó #,äÛÊÖs®™À[§øþç#0«UuÝt¨Š+›‘~Ǿy'ð<øvæ½X)WtÑçwË~ûzæøI2ÉBJ1­Ë#†b‚£Y-ÈÀ¸&*«¸XàPDGFØ€ÚƒN$âÆŸÓ OM:â¬A«„I‡9hå‘`Û¶ÒB3› í{•rÞ/ûÞKÞq„Þ5çŠ%UdVº]!0`©jÐðH°Â¸ûè)#/ñJ·è©5b0k ±Òôï £Âg+Ѥ£Á$}ê›leÒб™èÈì#,q#/cÞ†ýõA™ˆa+BcMTL,UŠ¸Xí¹ô@M°HbІ}±çÏV¦8)tú¯6“°WdÍ‚×gj“‡&½rÉ/Óá5),©F`‚Ø’a€¾™¡ÕÒ_vÀþ­ªÚØÎéb$=àüþMÁ‹:hƒábç ß·.« :¾_Hu‡Ï:Ë©"ÊcöM(pü& Åâ*ø>iB—+®4ÕS|òó›B½6Ô[ÅÓ#/ …ˆ£€“›4ÞjËÁ-'Á™óØÅ©I*"Êß^+ÈþªÇ<ø}ø{÷1á3zðé÷=1è÷ß)§³«ˆ?®L«Ïâe9š³ò‰.­'`-1o¬K+@ýÁ‚nõôã~ă–"aT’KJI>¦ËÅ0#ó!{¾éµlMi‘¦”‚&¢Ê#/#/4’v$,@ð’´`ÓTpÚ=ºˆ¬††á5BÇ ¨Á“(£Ñç{A´Aƒ×:ö}™›,;il™¤aPª °kºxDr,×l£ÌÖªÀ`ÚppÄÏF½5ƒ¯¦§&h†<+áÜÃò¢¨H 4'd#)ú)4jÞÏ Œ|˜@ª‚'SÑÄ0è¦Õ,•T½G$°NO¼P)ã6åç3Ú¦|ÈB`0B"ðl«¨[ÙB¬SIDÐã¤Åõ 2æcåÝÈߧŸ·µißôög)í[£CõÕIÏš<<Ó‡¤À0¤ ‚„1ð4‘DÒ$zÑa\ž¾*ÄÖ-?gÂó¥´ñi…¡Ï~k5£Qº™4Cé¡«a4¤Rbw^ÚaA¨mw{2´K»éÞÃmeEÔˆÙNÈ™Últ÷e±a°6›ä°j&@È0èþ5$åÍUŠúh¥^Qz‹#,ò==Ç îë7•³<Õ =ä1SÑGßS™ÐTX²…„¦1€‘A‚k¡Áµv„#´#/4µT²à€Ç,fDh¥7-çËûÀ]ÛI9£¼^ƒiJtK#0HТqAß©b È©#$ʪ1›Ýn»î—¾mu®ï£œ¸ø´á†7½ÇÍw&MçuÝÞæø4ø÷‘¼Õ>­÷5&7›"lQEU"¨ª¨lÅ OMj%°4JH©¥S®¤Ù˜b…9·gѾ|×{Ü’„LÝÚuÂBŽ<ëÁÕصÉM.yçZÇŒþ{‘$òº»”ªqÈ7–m«ƒs"ˆcLgYgîˆÓ-LSHÆÐÚ¿ZDV¯‰…&/GÉë]ìô¼9Ij0<P_"*C»b».a–åì{De¶Ãv=ӑßeg¨é½„å´?}RêTvÇ=ׄ¸Ñ^7Ü<2¶ÌŽNDŽé%‹¸‹Åôu¦ÕÁ44‚pÛæôeÔ%T —Ä=—ÜNi;ž‘MAéÞÓZã! õb‰¼õa”cå–q[€F"„»ŽŽ¤æQL#/#0¤ÅSYUŒ'oš0ƒi4{}*¡ƒ.'èÒ¹õ<,UIiïmEÝ×dÛð®È™”HNóÊøÕ{«©vÖÑÕ•$0ŠH¤/jŠ9«Ò†ºº™*åÌRøL¼Ê¯4Ýyjam–Y@21@xðí®&&`ÀëIJ›zòØ»8<fCæúöñÎ  ÅCÁ#/#/,_O„çØÎåK5Âwc"Zë@Ä'Ïs%_¯=^ÙfAfÎëj(MnBÕúZÓ£F¬‹–ÿC#,°VÚ“#56BL (MY1B(¢µW#6Ë4šl®º.T3ºé¥E¶6ß‘¶äEˆ­¯iF¶õz½›Zñ¹ZY·÷_Íï²V´Û*¶—©.¾%Ã.Ía;_»æ…™O·XLºífq!” H,€¥ÆP 2Ó‰Œ1Ï,X1O„ R9<ïp¹‚ÓÐbã! ˆþ·Ú¢ë\ãÁE±‘A#0‰>„øqçÈÙÆÎ:.XGÕ½–#0ÁÞn37œG×% ÄOñˆ#¿`´¥¬9o¢¡é$6Øj¨ü•J£-U=oal Ì1)$À«Ø3ŠæH<ö8}ÞTbŸ«mU5æÓMw]±i‘¨ÒV6ʼnJ @ °ÝëÝ+Äs0F¸HÙBòѬp0½…Ü2囑î¨p8m芀Dâ†VQ-åolÞzBmÚMò€Z#0’Ái)"'2Å¥<ƒ$ü[x c,¼0úÿéà+€‹µÏm&¡€m P¿y· J1#,¢€”®&€ßöÛq7õÙö[ºXÌ<ñâ¨Ë#/ôQBN"A@'äí4÷k²&&’‚_G߉æOŽ“ìËu­ýÎPR=¡:™Ò@@ÎQ+#’ pf-Ï\Û÷4'šè°å#œexóS¼+ÁEZArO׿ÖG **§!ªÀâC­¯%v•]'.pñtQñµ=ˆ‘o· ¯àÁ1!ý:'3U2ß#0jŠlAåáâ2ÇÞ¯Ñ@zý& q¿8‡˜‚"AX­¯Å5m;µ¹«jém¶,, TT¨ª¤€„2Gz6,À¼¢„‹ ÁdE¦¡èÇ8×æDè[o÷¡ƒmú˜3%t`¬Q`fD(£ ïÒ:LNÓšs>«§¬Ç÷mZ©Â¤MòRHô$›“Ξ´£G¤'$âlG•œJÇúDáÌ:$’‚’q vÙÚ[!Üaµ%ªE–‘\4¯!$1î;¾ŽÛ<Ô™¬Ò¨ÄtÓÓç¿!¡f‚^‡)e‡¬Ð /Áäz‚íó¥µî=uÎ6Ù·¼ #¬h Ú¯qve¦#0BŸŸeã”?‚Æâ*f, §5Hÿ=fw¯WJ‚ J~¸U V ½H§õáŒ?sZhÞßë/Ù­L1Ò¤X¨ªP3û®ÜSEC(y*˜”°–; aY‘€RŠ1dQÖ¿ágîf„âÓb˜A¤³–.ö 6±Šv£®Xt°KV,s|pa‘d#/˜ÁIx(”˜@¦mÇ{Ñu¡CD)’[ pBÑB<)iŠ¸¬Òc¤ÕYb5{ײ¤½ñÕ‹yÍÜøzãFÒ¨Z‚)"²iÙR‚’+h¥ “F˜-ëA„1.©fjC<(†bEjH2&´X@ E˜Rã…(d†˜‹!g4á‚ŒŒá† ™LŽŒ´µ‚ÙAiÔ£tʹ¬ÒåædU…aŒ9aZ,M꡽dXeƒ‘iªÎ²cM*1±´Ú£Ž2-‹•¢›»b湨¬k˜“óç½^™Ýv·ÔÈ[¤®DFq&tZg#/Zu‚­Â$¥ÔoüúÌ4Ë o‡á‘Í@¦²£¬ƒz}È®4MQIi¡tZ¥)ÆTª„#0X«+ìTz§­#/³ˆ H«%‰w o}ØÐXeÚz#,2aÔ”d qüF´>@}4¶ÖO*“ ×bnt!cì| ò¶éŸ¾ÝªflÒE¬šÌ%¥"•ji,PÄ i‰tb}þŸÌ”9âù¡Ï#0Ü{:>(y²rDàˆ@TÐ*‚zCWöÓ>è4BÄ"‘«¢/H‘Zúÿ ß»+ZwžVÉ©³5UvÛ)Q&Mã\–Æ+\­·R±kÛnnÖÉi¥M|îšËYv«Å"hn¢¬@E‰Ø|ÓøÜ@§è&pTRÆ„BÀÿfã´û®E‡Oà‚$@ƒÂYóçÝ×ìïÆÓЛ,`nqú½FkŠžm$H*ÑT£?t(-«AUE#0Dœè°KƒMœ'MY44yóçö}œwÍ sÓIí}?ÏÞùyÍ6@ùMÌOôDÆ#/à^«–Š+oÜîø»v·­\._SÍ6EAµ‹+Q*+h' b^»Q±ªMƒZÃd6ØéXØ®«»b¯Z¾.÷È^õ»*ß[mv–ÑW›ºŠ,ÄÂ’kRV©3M’øùíá–×Ȫø`‡bIU±”YqÑjV?N]Fêh£ûÊpŸD¢ã}K&qVAe‚¿*FU&`=î 0™I˜6$Ë!2ÀYc*ÂÐ÷ÔTkÓ,œ`óqØý’–n!lPü¡³Æc£/FÕ‡±IÂ{Û$6H—}ÆG}:—ƒámèÏ/¨úø±ea5j©8#XÓ¿ßîèmëÚ‘}(œŽ ¨V;UL\.ÜöÐUH.#/‡Žüøž´}½ŒIPPøi , ­ð ú¹DÞà5sê#,J¡³‡Ná]@ì<U{ʼ׎uvÝÅ%SiJ˜ÖÑj½›v¾“]£Mk4ÖSâWQ´‰š+)"[s ¾N¾½(Ow·„…¦é¹4Ç9oŤH@ÂUÀÐfzöwØßPõzvΑ8B1Gä?îó°ýgµ͸˜—6m-A¤}ÑJÆ&ºÜÛzfs»?d«Âeçž4]o”DÞ¬†ûç}2'Mª÷’$F EÙh¢ ãx¸ Œš%ÈV²ž#,—,8…l¼IT6ÌGï]þÁðò{zÄXD’$“1: #0?¦71q=^o 7 0”9¼Îž©C@¨ g½)á È–! IÑŽ0H•!!Jˆ`¿¿Ý´6æp’lèè&þhoS<0gQÒUɳšb?uŽ6 ¬Dðãu©QÒÖ•EU+Oì4º]0«,Ù†„Œ×rbßFÚîm„b!‰ýŒ©&Å0Ù#/9$¹Š„s»#,ji0ÒÅHThže#,>¼uý˜g» Òz3C„3q]HeŠ>Ž(q±é–9k£_øÂÁ2÷eŒ0Ë™ú¿ƒ3 &NF¶¥ÚæÄ:¢\ëoi™ËfYžÒŒ¬eR|᱓«¨¥= ê}©¦kÑ\6¨}Í'B¶60BVZbØ‹e¤¤¤Ù¤Ö¦Öš¤ØÖŒŒfšü²ºM*-,ýÖÚæªfU6chÔc4ͤš³jbX­•l­6²”Õ“kÄZÅ¥lÚ™K-©¢J¥cVØÚHÄI¤Y™øÞj?*aýïÒÒqÝ€k-†i™¢ âªtÀ8~Ô¢ !«k©ÊÕFÔmQk©m«•»7ƒo0¨ÁoåjZõ­y±œ#,Ö='wW Ä8(.Ú5ˆÔB¥MÛ”c%Ô¿VÓ|Vߦ°›#,€qI?YŠ¹’|9P"‹&…Qɤ‡Å¢1”ú( 0üôûôè|Z'¿{ÊÊtÁp(ëÜ]´Rñ!YóU¿O/ÖçБWŒV@#0 5}q^àÌÓpž¤ä…Ÿâb9µû &#0"ôÅ-D¨5îr —µ-ÒãBú™â³•â [G¡àO~Xÿn{MÛv\ ½€Ž$S½”ÈõÚ“Y*Rša°[#0dÁÒÍÜQE¿¿ËÈÃ56?¥šdbãã½þ¥SÎDì#^F ‘|Qæu#/®Ó¢US`Ê&iï5.ï^OP;È;ˆ#,˜O›q,1„X&­É<yIc_pqƒd09 u>ßÉuaË?¬÷a<`B+X‚¬40’8FÖ$ÏçZ£«”QE‹‹Ä Þñ?G¸ì,ù¢t,{Pü?)qÀŠ~ËJϧebÜÂ!Bl´²? <ô×ÑUçЊ®}Iˆ†,•»‰ÇDê:bKõmÁw¦ºC]/#£€£ KßõëÃŽxÝ¿{ð^m<è„!ì#,å59צ_%vW¿A¼Éó¾§ÔI5`U4˜.È\c#0HB ùáb#/û}†óË™À} š¾(Å,‰ 5-úK¾gŒÌ_¿Â—‹Þ­èIÉïÿ`Àº5€ÉÀ}7s¼¼¼Ú²*6”²›õ;¨µÄí²8†˜Tì R¦›úþ¸ð‹ŒxÙ†"(Õ#0`¸Ò³tc#/©n0–€ ˜RÔÜ°˜I icÓQ‚z´öÜ73¡êê]s#/#/-–Ûƒg0†#,ÝræÙ-"ˆcMF#†4@Šxf£ûÊ#,›¦XC<¨Øˆ°au#,#[â#, çYŠ(£²}ÝGoË>?Õü6ºsÒ^´–%Êg#W Û:àuÞo\™P†FäjQ…Í ^êB „éi(SÙ»SˆF%D‰Až¥@¸Ó}×6ÕD ©ùˆ¼æ§nôh‚D‚˜@`!b…Œ!E¤¡àS@ѲY˜#0 ¤âÐYÀ¡©ÄP2‰èòð¥åpMŠŽC°ïñ2P1׫šž‡wg‰ºª…‹"Í Q‰#/#0¦KgcÃ-צ“ÖŸ•2í¢zÙB#/›v¸%¡&aYçƒmÂr¤W´#,ÅÉŽ»tÍå‘¡#,ë¹!äö$Í°Â,„EbZˆÈ1”ÔPa®Í¿£3¿ÂKï¶ß}Z(+ñ7J¥y»[b5¿KÊó>7Í ¹ã¤Þ\É) Ww“ªR"0qUK@„raÔÔ³•Ek\Hˆaî*…"­7k *#/¦â7¥ÚUŒß2Í0dÍÖÕµ­¤âqrcš¦\RA„%>â=‘SGÓ2Fا7fÓ½®Àe{•`3ëò;Úgk-·¼þß«ŒGa[»•X#/"£í¤_vUcŽ††Ú 14®\¨#‘ l6õ¥„æo!©€Ÿ$ݘ÷BþÙå¸â·HF˜…Îä§á¿Øç&±9/¾½øáuÌEP+S—"BLD­4KÎJÑ”€Ô9Y]ÝoDÆÁÕ&1- ¤æŸg§ClI8Õ##/»pm¦æ#,œ„9dfÒ““ªb®,+V8Ó!´(ÕçeäÎj@uåÅÉÑò³í$½%,J~ÌéÙF0Ûêð­N³–S©Ä¾ÞÛ‘>"ž5_¾­5w"cÙÍÓL|3’÷àÅ”žÙ>±ÕNÕ7/¿üÿäeryøúï"fœ³S°×b#0ÈĪ§‘‚Á%$L HªÝƒE!Kh6-€N bÍ¡æe[áÚPxéL‚žÒ+b*h‚ë¿Ó:þ´EyÍ»¨|;¿›CpV ^½6äð:´lt°PÄßÕé.`*HÈB'†%tîT!œ¯]€Ü²Ž‡+-ÒI Ä#/Tƒ¢ým#.Ž^:!–ݧVyPzÐû±‰°¾‡Ùæxì™bždOGw*±¼‚K1)¢ôä È–Òó·5ÊóΛ¯<¢Ý㶻CÎÖºÖMµRZÒËžJ#H%HTŠ2¨”c(A#0†~¯Ùì=†ÒŠ `b?á@íÝ[ø."#/6¤Ý%Ôh¢ª˜üeÝ×îð˜ Q ˆÅhŸ2V´ÒXÕbÖ¡CèÉžÂ×íë#0mÝí½u•ŒwEÂÓ.5[jÓëí\¤¯2ó7Š2m¯.w$F¢‘pÂaË#Z]³Z4À¤Æ˜["ÐÁ4!JRPP‰l7¢mpZi‰£–cÒ„Ö©‹³P#XCd W6¸Æ4òï)‡GDÞAFm‘µ¨#0)‘³'Ä/ñv­46i |=âÁŠ¨…€Äº"¯’hbÙlÀ0!”sÚ¾ˆûÎvÇÄÍ‘!…RH nA1ÔìÞyTñòû+,õ#/ÿÒ#/Cˆ—Wžº3ªaæó,ŽîX—ICLT€_Âe8bPQJe1+›´“©U==BP‡LàZ5_™¶®Í¦“e¤­™hÙ-&­Rš¯Ê¯Ömú­i2ºí¶wså·+•Íahˆ}]iØPoü{†?&ácÆå¢æîmª`xýËrˆ(°ò쵚²lÐ"œ:Ž<7f^Ìqà#0äSáôÏyGSGÀD¾—#õHC¡Í¯Jpƒ{¨òÎ!W–Ñm½Uàd’bŸ#/#/ýÉd[Aeæ÷O…†¤ðZßS5¸Úa$Ù6VˆIÊ^Åì‘'o׳.‚c†þó{¾äúÁ…_(›#,æ ´¥ê¢fHÜÄéÁ8aA—IJg£3ÒdˆÂEF1œ5×=9…eˆ˜’Ùå‚C*b’˜£FÀÓ"…ÃL;™*ÃA#, •·4ß¹ç@1mËDŒÎ(ð/lƒŽÇ*˜Î¯Q¸QÁ É”p® wXj‡j8õðKUØdéo#m „Ã#·Í㉠ÿGœ$Êe©{|ªUË1Ïg3·ÃÖwˆiç|Ç‚¨>Ì#03…Ç]§§·ë¸zâñ‰IØdÇâƒMÚls¦˜FœÅy‰MÅΟƛ¸lxÄbpÏ’6‰Ë,Íðئ£rêèáR³‘4tC“áB³š»sô˜ÂwÕD“Ú’g'áØã~s³Õ³“Ü„åÀ‡.Ô¿P”:ûÍCÂ!š¶#«)æÁ… €¢’Oրї£–ÿwlû·ÌÃs´Èg«|\eD±ËÇž×';~W{vßrÚÅöwF¼“ ’Ù…ñ2Y3U UT•"Äb#,Ñó™Á`-q÷ê|q–#,\`¸#ðìû õ`´—T"Åx-ª,,&†W–R´U`H0n¶Ñ;ŽŽL€i‚0A@‡Ô2à ˆ$±ÁÍ)PÃi¡ô^¹;ŽžžGN>¡93‹¦e•`v~Ÿï`cø7I«Kgd• x ûÀKóx#óð€„¶U†¼Xd›Ü\1–ý@P:J‚¦K4 瀉µD1Îë÷0¬Ó™£Êy:&xèZDzš<‡ApÜ\!%ªÖ¤·ˆo€sÑñ#,!¦<±D:‘¿µ¿DeŽßOÃjH*Ëùì44v #3^øŸsónBD†’EÑÚ£GÍ¢¯WUCní«®[—bÝ]e¬ÒÚi[E©IK3VV™µWU±£\¦^.I¥»jßdB$„`ÅŠ¨È¨²(';MO!ßÇäûZká(ŠcØ™m™8-€‘Ëõ¢l›cÕ|2ÉÃ=R‹5ix|.æsEÑX|l% ¯'!ÚA!}M¹Òa7JYU%D]3ÿ<Diôˆƒ#Hã"GQ›I†0å„hÕ‡åÈvêµgC)d,¥1§ÈkpŸÏ¹ô ÛG#/^m¼›v”bmÌLoØ‚êYÙÁî7)‹+OX6i^¦oöWY·`÷¢ PSöW˜ÀÔC/J1n‘âtzu[y¶Jíì`ááR D½¸é4Ôµ-Ì[¤è­[ß *Ä`†*W)[íP¤ü¹×I5É€\oVºˆf?jk›#ØßÖ_6#/ˆ$,Íûcû?Ùÿ?õÿ¯û›ö¬ ú”)Zr*5®m9¯e#±`‘‚’í‚|;i-R&ÑñÒ®­ÜÊJBˆÀµ¶&Œ‚¤?I¬½ -£«&ÐÔ»©ªe!^HÛz#/€5  Î1ص#,âS5Ä_,<NðÚÞèêcñ^&-‰ƒÞô6\ñB)$‡é|“Ž#’:oM[·"ªì¢ñù§üÂÏ'îÙI"äso¹âý·¬ïeRI%äÍ ¶eƒ¶$4Éöí×W³ƒù_¢ÈÖñp™³ ÍÕ˜wÉ„B÷Lo«#,{Ъö@æU‚<ž6i´ؽ¶#/Çîb0ˆÃ¼;àÎäºt…#0;tšB&à(y—IˆõÀ²RaF|‚fgRHŒç[å”°cÓ’â_Ìcs¿8¼hbi³àj¡òÈÿ­0¡”Ìòž‡OÐÔï #/C¦Ó=‹#/?£R3MSd·†ŸÊ¶+Gžõ+¡c(ãö¾ìi#/‰are†æ;v'\$™™Ôú¶uhøh-‡(¯JÀ»{hÛó±J&ëO³Œ!èàpbl+{­º5”‡By©žlù $|Ï 3r//–íóÕý„™I÷ ˆÈO¨‰™ÔzO^ñ1#,ùtì¸à'ªI-@…A(œfÇ‹Õ­¢¨Ûm%Qµ2Ú“kZ¼sj£mrÐ;ã ±ØheÝó®]+•F’äBZ’Å@(##0üØÊ«Ùƨ±ÆH1<Þ|OfŽ²ÓíL´1Ȥ†£ØŸ´’˜ö{zÌ ¨Ø¬óè-Ãê8á´ÁC/®ºÖA¢Ÿ…×qƒ8—6Ð#,ºhûF@H#0²£ÄJRA†¦ —žðÝÝi:9†<áÜPŸ˜|9©Ûÿ›ô°Ÿ¢ USF§_>ýÎ#,t…Bt(9˜ï#0Jðó©ãåšxiáÏè±i0ëöˆHÇô8ØD ÜÀæûÜæ'²ăöœ3Ž]ûi¾„b1¨zh{+qQ “Š„ƒs2ëG/Li1f­ìÁ´›*ìMSmÚQoöï{m¦ BoI8öCÞt>iO¨å•É³>ø#,F˜LžmÉ ð cß${ûªõàÎ7r/H™L ”iMAÙ†RZÈäKIn°@àò¤#/0/¤#²äAA‚èäHÙ‹"#0ùAZ#05+S`Pd$ncñØáέ³0.’ ;n#9†ðÄŒCdT`Œ,»”\n)õzüïK=Myz8ºéÏ3 CXšJ$J²DÖ m°bÄd&©¡=Ié¯^q‚ùbî8«e¹þ¾Ê;*d–Gu‡jùš˜ØZ¥„r‡—K%m,N³÷6#/äO3È¡ÝUÇ_—ÇÁ>^1çf%‹@ …ó?eÕqÀ±aMU<ˆ˜‹c¢µÓ?ѧ¡uPoòô„^Àêk#,L8¿]؇ óŽ W=AȃQ%ÍtÇƹoW¯>;å¼Zæ×5Ýq†#,ѨŽ@Œá”Ÿ¶´ø4=¨Þä:쮤SÆð«x»y7^[®¶uºfÚJÒ™›FŠ‘`,(ª #/Ê¿é‡#,ia1f4Á€Â€ª 1’XZ18a¡ÁQkPt Ž#/#ÁêÚ'ª(Ä8:5£*`ªj$H%UJˆÌ°*Ð>VPÈ ù*…ñ΢O4ÏÝ-‰DÑx#0E Æ1¤6,šéW7Û+¢Í“V’®tƒ,‰Å"L416„íhÊ2Ì›39APŒX8•#0*ÙE&h…×—ÇǺñ¶e0›œ­ež¤¯w•u¦Ê™Lh£R¶R±,3LZfÙ$™´Ø¥³2L5²–—½Û˪äÓ»œÛ’¢›®®UÊvë¾.¼kÍçxMŠ•C"å¾Ï{â|3a“LÔ<½/8ùíó¼øÛÌ”CU«à#/†«7L“­<Ù¬¹†J”0Û)ÄræëŠÚQ·I¤L5¨¥‰±Ü¨Š´p˜h5ó¨¯H²4®ØÐ÷´IòãÁ‰†{h·“`Ø@ÜSr0Ó\3 PÉ´7Ó¦SÖ9Ê~¼½·•ÕclÚ½µÒ,fßNéY©-}Ò|W“×9³2Éj±m¼âé%4#‘rÈZ#0Dbµ› ‘(Ák†Œ†@JJlÂV‘ˆR+†š…U´Ã‘‰ cxÛŒ ic$I˜Ùi-+Bˆ‚°Vês[`Æ6$ÝLhc3µ1†o×—8áaÆð1#/[¬¬ÂÛ2qŒÚ¡»±×#Ya ‰¥Z &LW=y¶²C$_[#Ó>ëL“aB4SÑÄ£Ö‚&ìÐôÏ.rá¿Ü‡š¸M…:USÑÄåmLÒ‰aãå1‹< VÅÙpªVÌš‰³-h¸i-<LEà X´Í··ª Ä¨¼1#0°0 ùdHøšç£A~óuÍ$ú˜i‡ t3õ_#,Ç¡ü=NZÈ=Œl€f˜Ð¶ôæ$™Lg5R¤…¿Á4+¸&[aXÞDFF(ÎX²C‚hÇŒò.(²dm†cfÙ%Š¥¦›ÖA5½oPš{ywí»BaÓZU‹f•1U—¬rØ©Bø-Mè1œmávßW¿¦³†=™h¡…#/Ы(~ËÒ­ÂR.°Ó:-1Q'*Vác(3*­Ûß›› Ûñ —(Š#,c J""1!¥ª‚4†Àƒ@šTŠƒ†#, 1(62ƒE!D°6HB*–(»’`—!$¸”_÷0"fþ=Gœ:£ëMM þ¼=_N“;µ%—UúÔˆ¿ßn¸—võ#T9†>²ÒÂõy‚¶©…ÆÅ\ï|Ó&M{kc¸{+åtª„Ùý)J‚0‚$žï›J‘Rš-!v&¤OíéååX®z懆´\¶Ã×mç³wS%,íîé;3…gž¨õgÑu—ZÍLlÌêÔRʳ\Y’œ<KÙåКVO>ºês_Èû«7u¹xr¢2ónN£fó¢ºç3naC-‡8ƒFbh¹,½cMK¡‹2Ž9Ø-¤ËÒèøk‘¶]W6ågC‡Áù§ÃvÜÎýýæ‡ ßž¢è ÑFœvb #ËÊ´ ÓF$·ì(¾‡Ùf'ÄÚØäõ”yÓÜý:³³¯DÜo¹¯..Ýš„’×5m¾0ó‚ž„Í7eES-#,¶Ùj¤3«žQÀ®ÌªýöRÛBp(­™ÎV¯o†´ˆ¸±³˜ï¢Ú¾ºpÊÐM/<p;²5S)ñPô‡œ"'Ÿê˜yÙqóÄ|ài¼XŠÆØJ¥·×_ÄÚVω•°´·³d¦2(&‘#ð¥UTƒFĹx7ËQâ”rI¤m!¡ùæñæ׎–7Š®Í[…¶å¶J®ñѱV5y×.šº¶å]Û™ ¹xÚŠu4–6‘¢‘•6ç#0ÛhÕ%eSzÛo¼dÚÒnš×mמxÅ¡6•bM 0#GÁ‘Ã"+ŽÌ«Ýb£¯L±‘IžÜ'3Ý„ÖE´’+H€ae¡QaH4ÅV kÏ$Fº—….øÙa˜”_;#0žt—7+M|my3D¶ö‚D-Gߦ5Âm¨ÀŽ$¦ë_#/ª#E­çv¥)ªIþcªŠl¢¶+ ™¥65ÆŠ™¢¢Ú+¦V "™Q²li”“1•MmLÉ`#,D.´D‡‘J@Ü{z}øŠFFIû¿‹ç]"HÍÝ÷µ«^üA¤¶Ñ’0’Ù}çNÛý“ìð¿w™éy-êþ^»—?¿ÍEÓ¸°x&z8m©6Ž1¯^ÓÍA#%k~³SM­%2´_ròSfÅUùf·âV·#/ZñµÒþ+uQLóuѪwvék»IM¶¹m£&±[º»JV´Ö·çÝ_nÍeEª»i·n»D¿í½?20&€|Š4ñ©'(1L˜Èeo2HŽòЃ±vàˆÚI #0 ™L£ƒjRÜuóàÑ)#0#/¥wáNbØH C"áÕ×O&‹ÆCbÚ8zÖÛµ=ãåƼóP<×(ÑL,‹ö>TVh1.Âê´a2"Àùf›’@úÎñ$#/ÜÕGïg#/k4‚èYŒÆcsñ²’AŒc+¹’i¨LcI0v™mM7Gša™&pæM¾ŽÓ)Ä¥µÞ0nÒÂ<ý›Î=~™ÈÆ6ºs+G@Èh‡C&;dé¶*ßÑ=˜ãTî°…#/l‡2}åt—#/˜U{FKB˜ù`¨¬dŠa>G;ಒsàÄòð€]œæj‹MÇbižl¬mƒÈ"Vz˜uÂjãL»½žçƒ¢ÁWøÉÛB±X|‰Ñ€°è:õÇnåd>—aºò¸Y6©²q²Ùùñ´ü|hUcÑ‚ rb1ñìšL약R¢‡ª2w¾d>‘v¨3 …8À~„#0­‹jM·×Kîj×JÒWöþÒ.7¥G‚Ø™AÄ%å‚Á °  „°¢ù{o9¯;]~ñà²ïè¢ã>-H€  _›ûÊCà΃õ&o0À¨B„"$ƒ#0)˜T‰"©*Ñ-ÄTñ(Á¨4(4c5¸ih%µh(hûžÌ1ˆ~0Ä0T"Ü«ŠÙ¨,d˜ÅÕ^,ÛÕî*¼î×`r•l…AQI˜YeS'kŒ%XÀ¹$c!d‰8 ÊƘÁhã1ºÆÙ”@F€Ó0m2ѶjÔ¥ˆÒÈXÀ¨&nŒà©!„À¢À`DÂ…",P¨Pµ±`4¼sˆl,.#/©±ó¥¤µ]¹ Ì@/\‡¨>û×Ò )#/1ˆ‚#0"0õÊy?_)P¼æð…í*K?A÷ažäEµNµ´ù‡k[×síI¥ÙªÜq±ÂùÇ †´É“*ØPçgðéŽrÀx=~ê¼Ö«kðʵ±[Wâ¼Ì!è@\U0‘B“­ïˤ÷9üãVHý”T1?óüÁã#/q¼8ŸT2…Ï›+¾3t~_ç#,‚Œâr™êÀÓj*‘*˜z*¬!»}o¹øËãN`Çá#J&}¿–ËWYÏ›‡ìPQÌÒø|S†œšîݹþV]ÂÙrñ™$b¢¶u¨:/p;b”47}Tên°ècëLô‡k‚€ohD%Xi¦Æ{Î)½°3Ÿˆzè-í{ª뚌?«wda!ˆkTTµ4–ÇœP[TR”<ŠV&ÄIV†ìªR[.-‹Þ¨—ƒ÷UÒ9å³”ìðëöu–‘û#»‰ãòšmzŸuM“pùC›Ý‘¶Íêј|H©Óœg *zTõ#,¢þ§ô0ú¬#,1íç¯oÒš.îuÍUËZîÖ[©nÕÍ(bý±µÑP»{Ô”!QjõÁ2{“ùŸ#,ClnT„Z *Jd„`2˜ D$ö›#,ð?&©ê"¤/~s»^.ï;¬Œ¹U”uÝâòœ·2óq´ÙdJI¨M)ª<j»Sl•Š)60xÝ#/6æÔgé¼Þ]¸ë®ÙŽt.Wwn‘\¼[Ç’¨å4·“ÉnÜ»šÌ±¡ÛͶò­m¢²Ucm)ciå¹K3IRVy×t—Nmݙҹ"•;®Ç*ë.u›FŽj¢×j[sÑî EÜØE.ª¾´õ@.˜& {6úwƒè²Àö»I³•Åå@äÞ§wrˆ#!ß@@„íb%€ãöøˆ‰Å:qQñýs”ö‚§`áz#0A¸ß‚Á^9>˜õøBzŠô_S_m¯„γáê!ç˸R}´ P~Ou·'åì9›N£Ø…‘Gt ©"1Œ’H$‘0i¦†r*MÄ€¹¤‚²È¨Z‚ïû‹–?¦#0Œ` œéöñ°(˜@KÙ5cUhRÐËF¾b!‰Ðz£}…!è#,HŽu#0@„¹Ú§]wu,¬XÄÝÖ»Û´FcmU64Á#/ÚWrÄq"‡¨¥@̆1 ÑZ•È„„$O{¦L’h6K嵫íëuËW϶ÛÚãʾ3ÕJ#d"Ò#/¡xªØ„€©E#JE›"Ø #0(i„H£#,ÃC¦{7ï\ÄÈ(O‚¢Vuæ…Oðâzƒò±HaJ¤b°J0=Çñ ÐõV¹gHhôEW„Ad"‘#,ƒ(1"#,$ $wxŽÝ~&7:ˆ)³1b°€AH0HÂ1DT$Š?1âÀ7¡°›ïß½õüê*ME¦XJµüâÚ–!a#,`& yÀ€õi³Âê=•Æl‹ªˆ‰)Ê#,ì¹B™«ýƒð;ƒÒ5$“hhm=>ÞwØŽ¯I¸éôw{¶îü÷Dúî$bˆlGü£ÌÏÅÿé#,¹/Úƒ,5ÁOåNA¬ Ð¥H «™!wD¿£kjråØ„B,”;;Žt½ñ{†1ô¸~Ñ°¿‡yåâšNÃÌÒTž8.Ãü#/4ø!k6$cC#,jH–:É-CJ‰Ç„"@ä@ØR&7Øä"åÆ `Að6Óal…0(F0#hM£{€¨ìZ„%D!#, „Xó›e46<ÇBP «ÚÅâÕÍŠ¹R[—Åš­x!$´IòulUW{²óŒc[É­™>#0Ò{^,wgeUƒpþ`¶´ $a˜ÃI!´¶Òàê<.ð#/Y$5¯Ú*2É“9¡Y ©Û¦`…©íã5VòUvÚ6¨«bµ[Û)TT nòb™ÄÍ#/qL±ä#,l)7nÜš… hã÷ØGòŽûµ#,¤óÍSƱè†÷è~EãxQ¤²D*¨ ¯Æ ¡XL(8*ÔHCZ‰ ¥¬gáåN¬7ýÐÉöÄâHÆ@Ô Èz =[ ÑPYT+#,‚©#/¨UÏÝè#0~("g‘•õE™+¯m;ek–«Ì·šÜFªB+NÆ$b‹HLЙԿ9넆ðÉ=iÒÝSzÊŸØHBÚý©øýí´s,¶´0Ú ´¬Òkù) ××*ùµµÄ_Wœ¿ZyÝz׺¥Ý+S\DVA¶Û’Y1ÁBHÓUѬx2<jf7d„T®þ¼#úz†Ù¥¡EX3“¹ÙÿUdàÙÀsP«/*­¶Õ{´ÅyÜè^‡…šg‚=Aš>"û'å±¼6™¡ä#žA?ƒ1>¨êÄ<e@#/³óUqÔèÞ#0&áËdD#,¤‚ˆOU#/0XúåHŠî«Q@éT{®f³‘s:loá#,p·Ú¡ÀvñMonøÃÑCÂwCî,UÒ#0™FŸ·­~ãZÉ÷·—äm&Hhü#,TÐ-ü¿Ñ!;íôÎe½E}Þº½eFŒ©_‰u%ƒhžî÷8~9,ÂUp^¼`ÀÏr(„£"o>lgsI9vIqÒè¾céƆm ðWùT/'a­½÷BKâê`¸@Ãu8ÄÆ^‘–MìŽò:ïÞU*¡É¤èîÍ‹Šá1sàÂb¬wE3‹®XˆÖ™Rú™u(2‚¸éR «†AdÃ6ŒH–Ÿ#0/¹;]ÆýÍŠ%ù$ü?9/Òv{ÈMI™˜©„=©#,ϯ^¡#0š¢àÜïdf#08®xTÏ\s,®hG)jpN[˜Z*ŒI&‡áÙª©ÍŽÙ%슇y§!#ø>ÊÓ[ÿ¢b˜û„YGjئt-ª9Á\Émgó2ߟ´´7£L€ÁA€ŒM+¾»ß:¼g.9׫—/”Ðv8€ ²LGtÄÉ‚æq¦!…“Lf&Š©­6w$îàÕÈhl0¹}ï]Ößߌi‡A‰6kQl €”õqïŽQÖ¦—¿ù^tžpÁo4—ÌìWÁî'to°s³Z Yù²väZ½»ñh%É iŸwMã.²º<ðme›¬2¿‡ÛÿUãÑ0™‚Ù6·\“!Ä‘Öä7}éf£uÑ¡˜„þѤA°^~Õ®· Ô=<·ŸóYK¼ezòv‹GMc’pC\"‘„hx•)ù›"ÀÞ˜a?šÆá2ðæyÐíòž²SdžF§¾Ég·Tã÷õÏçz¼ÒK¾“Ðõ¤ v9%PwO_—-µ§ô—;‘’p‚æD>ü´.Ççì#âh(kéÎ’‚¤Û)IŠøo<MÀß©&ÔDèÙþwÛa2ˆñÇÌ„öžtúH€ZŠ¨”˜A´ÄAÍvœÊ:wuñc€:ž9Úwô›—m­×êoÙœºSV+3T#î`#\{gRB’Iªýÿ°Ô#/Ÿ¨b#KöÞ1ëZ¸Qky6Kl—5s±iz¤‘œ[3WoF©Kö±úç¸}·4ݘQikZWȬ»õñëé­¼3¬¶',+ÿ/[ÔÖ%NEúrßÂ6+-c©šc50ZÙ±N›Ô‡líÖ8Ñ‘‘#,â¶â%>6öca96Ø5#0ƶ’"HL0!#0@Xy(ÁÓC2aE?=~¹a¼²>nå¦zæ«% åñ&饻¡ô¾ ð¥@Ó¹±£ÛÊ3Z.ZŒ!Ø„å~{w‰ÁU ™8¹ë‚EàœÁ‹)—~ÆØ5ß#/1P´ÔòX¨&õ½´ôæHI$š=»¼x_D‡nOì"öã~‘RŠMÎ#/(Bd Ý @ï˜LFñ§ßÕëV_*;@' çöC#‚’É"±ëS±!ïG /í‡Àù…`Ýí #0„‚$",‰…Ý¥ê຀ûìn‘ꯤûëÌy“FêÁCoW Ç =£©«´ö§d¸+ñó›˜hà117#, ä{|EÇŠ;¾ÁÓÖ}¦cÝ8v‡jqñp‚ñ¢W—ÎÒJTÇ;5m[|âZí-´Úüi^EŠ rPöÀP|FwÆ‘rÓöW»„+GAÀã —g%=]y¶œ”DÅG3 Ÿ&85ìÅG¤â0‚ÇÄF JPDHu©}ø )"*B0‰Û` B‘ˆØ‡cPηx[c=›œuj:„F7Wû\ô!:RЬLcJB@s…Æ-kçÇ÷ÙîfVXâõÃøÆ‹Ò$¤lèÊÛLíR‰È}»uð E(ª@s‘ #,XAâøÓyí¥Àžn˜½Ã—ê7l;]P®’81žÆßã}ôÐk3>çVgS9ÑÝvôcP¾î)¦‡þ£ò3!NÄ#f^i¬–tZK…õ⇠}K­o™‘+Tê."—mßô¿†ÞЖŸî÷^ýç(V¸³ºþ¾xÁë›Ó±öݺ\çF­¨ªÊÉÀ¸P¯œRtÐ:ÐbÄ„C¡h“Q¤D·[–î¹V&ƒ“$!ô[ý_=µµ#/,ÑîÌ0,Èü1OtOéËÆþÏ</EQU O²QfÅS‹”’9‘/·.±)ŽäöoÖ;¦õŒªãML:p@/SÔúáéDÅÂkÎ}]–IƒB?d3_ÃÞ¼#0Hœú•I¿ÎŽ’1=ØSÊ#/Š0ëDí=x/lBs­•'l@ÙÓÙRŒ?fµëÅÍØ}Y"‘DDkˆ‘ï>­ñoÞïa[F8î®V»ŸÉ»U%Æ*Ùl¦–Í›&Ö g^íl†ç•hÉ0܇Bä±4ôíõýÇ ÝPˆ(>”ƒ*”ŠAd#/Š¿{øßn÷ØýïÍyæB›&dfÉS¦Š KD¢ZJ4“J1”Ù6&…!&Í“e HÆH'Íwìžj€Ÿ¹Åè‘óôEõ&ÛÉ;·^gݤ“fݳ;ãl4&K2É„m1¨ŒácX¹¼sÊ#0€F˜-°ïRàDÜŽcÖ¬Ž¯ xƒ¦m#0P!¤”*­kê²ÐBͲå!<«×YJ²«E9ª`Ä»bÈy²sTñ(BŒ»%ÌàL #/[må›™ép¿<6Úé°¬0»v]ìÞz”:20;÷C±j:ÙÇÇÚ̘–S[B#Ixî<WÈý:´¼Ê‹'Ö81ƒ/Ã÷†èf¸Ë¼5H6 àÖÔxÕm &kükß}jsµ8|ÉPjM7Í Â~ôDH-e°U1’•ºÆÆsnur·¶—“ao ¨­]e™Û¤#5Â2`‰È€ã”#01»²’´ ¦’PpÙX1¬’¢R¶Ì² ¶Œd#/q/[À$ð-+tJ!vŒD*!ò•6’á«ûf4„ÛzÔm¼ëµ¦UʸréF' £IQÃ_^hɵݙQÄO&ä…d¢@¶MP#0‘ÐØÄ™Ve®ìY˜Ùcyj~#/DÇ£F rV±• ^Ïi®«€Òg|‘»‰±q4ÖTñ• QÈÌœƒš<êKøµ‹#/êP+½M¥aV#Š) ›Ã¢œSCH°y¢¥ Ñ®#/•ͶÒÁ6‘\#,ÄH³#³5A”¢ê–"Š‹uÊ’‘¤Á¸EX/FÞI £§žÕlFajÁ¦Œl„G¸­˨§4U!†Ón9Q dj&Ô4âiãBÍj¶65‰¸IjÁA´=#/É„#*‰•Ê«N–;‘;1F9X#,#0m„+‹ë#0…\ª'bB²Z1Ž¶†ë)VèÌ%(ÃRèÜQ(1Q°bT‡K.z:7#ÍÂã€Þ=ìs.îö‰««¬ÍbÈ´î4è6ða4îñÖ´³Z3Ý<cfó<Ñ^m8xæân$;r”¬jŽ2´±°°¬’VA–¬j5ÕÁ¦f0Åd‚íÇà×u j0}=¶³Dº ‚ÁµÃ8a™ïXV2!êôCMSLŽ¼V)8ÖÔZž7iŽ@Ùºc;ëV`7p¤8Û˜–0O#04áLƒÅâ£#¸TqV4 ê23ùN XÕ¤ãT`hdCJ‹t.h„E³(5¡²UL)‡Hš:¸C1‰ªJ‚k?¼ OE“] 51hMâ0v«@‹²…Š0:Hk%iEFW® #/,i#/†"g7$†#0aL0"WYzíãk²§vÝ.›\O•äªïá0tÄÝß#Ã2*ùH åáˆlzé寥´¬[ÌÕÖVòë]4°µê©NØ ºCëhÖh\Ž‡6±dZ*mÉ8~H&Ý¥åΞà›4!¡òTk‹#/\nÁnFXÜÝ'Cd÷'œl#/^B¢WÌô0ÚÎPA’ï‹èëÜK#0Ðæ"%r³óäÌ5)t›F¢*¤'¢Q#/£Ó\‡¿Ô“]šäÖ1@‹uË4¨Ž%EÛàÕƒp0<]5¸Œ6ª$ˆ#,h‘±4ΦÑ%¤lPD—$Ò#0AP%‰*("ŒŠ“#,Ò6&(4en ÆÚ#/‚ š>ÔÑ"+øo8tA•R‰iCsZ½ï{ÍtÕjïzºýþÍÐ{xŽí;’Ü·;ë¾Êþ¤ ÛQm±BÔÙ´Í–Dվ˕ª•Ø‚€û;A²Ÿ¨q^‚ÜLž—uÌõæh¨ÕLUBƒvà‘,uà#0ê}É–7½è¨ƒí”ÃJ'•lúdªÈ¸'ß·ßÔ•T›FÚšf³muš TTÜBåög{'²Hžº”•#,äwW´M Í×O¦Î00Ò,PL …Sëw¤)hÛMöÚHDvˆ75¥Rƒ¥ÆÞ.ë·“ùM¼÷¸ÙÎØŠ66Ý]]*Ûã]t¡ÝÑå5©~Ýx+ц%#0¹˜6#Š.ÄXÕ4é‘BTDûä,u8PƒC-"¯cG.çE8 ÚS®axòà}kan…†ÀzLÛPcìï%p—ç¶Íu>WÓ;$"ΊLß•ÌØŠH‘fŒà*U‡Æ:A»„öÖKܧnx_8Äp ïmº¹'&ÀêP2ùÿItfH“›ðˉIÀÂ\µÊ"e :`]ÅÌ’²ö€ñ4L#G/ÅÓµÈì‹ÛkY<nT’ô\¢öoE²ÓlßMyÎU>[:Àþ#à—@ËgWäGæuaÚˆ7Ñú}Ï¡ØÅWÌä“»¼]qo{œ'ôbÉûÉ|õï¶ýZ&„ŽôЮ;ù„a#/&xQà`¡Ä’$„®ïŠt‰‹–o´é½¦ÐïÅì½CŒ¢q‡XÛQNÁX,å:M,TÙÚPõEB¢‰¹ƒÎ·àpüuçgq@êðû‹ êËU8ê_Œ€“°-‘Ó™Þk¡6j€L¨°LU _¸Úý¦ÛÚùÞ/ÃÅÜJüÝÛ\ýLª£A÷¿Ì~íÞñ­Åê²DI†s‹6´Ò8zŒ1ˆWYUh\¹¾çL7é‘؉®3¦“oÌë,#,ä!lšŠ·üÑÀÓ+áMÈA˜ÒPcr%-'«”jõÍ·‹]5¢ÞµnÄ×'UxÚ5Ë\¬hñ¹µ×mb%Y#/cQ­äÛƯɯm;¾[‘£Y6Þ«…Ñ^/ZwoUs[¦Þ=U° Ð嵃DÖ+å=Áu7”Þ‡OPyŒP½‘£P²’!Î#0 Ñ;Õ¯HúŸãÀq"ÚHÁßÙ·uW¿ ð4 àò‹Ð)ì‚RÁËîD?i @=Óùé%‚"; H2#,UwO·òwŸo5Ékvik¸ÛuÝÂìÉ(ª¡;{‹…wÐiuH$`Þƒ`14P Š¤ˆ²"¢B(´­­t›¥kø_m·¦½ÞÔ¶#0LA»kÇ‘PwDˆ#0ô@ƒBEPwÇâ¨pÐE6M ÿ×#,O8køD<ÝTvé(ÂÕ#0ÀÀ"^)ˆ'žŠX˜ÞT©å{ZPuðé‚J^( ¦b÷bÌ~ã¹#,Nï#T¬´0C èį¢¤NŠƒÓ°2böÄÂCâ P ûF©™²I(aŒjY#-M%#0Òch±e))&d´PÍ«Fµ¬m¶5jm6̪j52ÈY"D"‚I~áǬ»ª€`¶³ VÈ‚191‚•È‘>¼)pn{å{å×..W:uzéïk²ä•:ÃLb(U¤@Ž™1ҮěCeldEˆ’ d• (UF LÒa¥®Ûš•’­ÚU¦ªæ­T’«Zê04_PDj "@'Ëóåk ¦´¥¤ƒZbªMIÛˆ±„F1@ÉV$(•‰Œ*ÑYhuÂâ¡i¶¡Ѥ‡8ü´Ÿ6óVtû¼%þ_F}5¥Ï-R¨¥œ@õ#,„„R‡€gy³ìÌÎ0ŽÜv@ñ8C¼0¦N»>#/–>Øé---,·´bR[â3b¦EyóçË­{¥¹RöTÖ­ÁÉ‹?O¿z’Ý$B¶C¨A¢‡#0—ábŒ ¥V(VpµD±êN Ö‡hëîÙ!DóIGãÏ.ºk«Ë.Ê›3(_¸»ÛUÍFµŠÈ""@ ©Ib!Õâà–9©«&ïàŸÔ¬%Ž#=&(ÕðtÅcJPt9ö/ãŒ>×C$ÚT›´¾“}‘pkqŽ6ô‰?+–…ã\¿Mªgùºo _Kœ‡LTæ'ð$ØMÕLƒ/_<k:F“%#ú•ø¢×50tÆoNé¼*~/jKtý·ƒ²z®¤Ž.8ésÇ•ºÖ]¸rSÃÃôv˜'¼aq<Ù9Ü¥<ã~Ër/¢Žø%aäü\µÂ)NJ4/Ï‘üÓtç$snÍ(ÓJlç|]?«^PìPË8i­cw%òxì;ZkÛíŠòÏ=ÐìÍ?5·fyFi="•#(ï½7ëFf£ŸN{Õ°mŠn=ñÖ¬ÁŠ©féKG»ó¶ü0NJtwÕí¬zæUjW0‡É]x%]pÃ?‰…†~/ZYLÄ’8œn]´‰ï§¶@ßm¹‡¸Þo«ž'vÍ#dN8¢!ÈfB'‰ò{i®–Ø{ÕÝAÁxNjg¶ïrÖ¸n„$Ç:“pTý2üP@ƒœm÷w =P>Ï>~ù”šiê=uv‚G³Ã[ëéºFÉÊêRP¥ŠO5¨©PÞ@]‘AÙKî¥7ÅH‘k»ÄzÍŸIbW(âš­É]Ï'mOßbt±G°¸˜P-V¥©ë…ŸñõØZkpí’^½ºL¬ì©^_°ŒÆâ^Ü—Ȧ¦ ¯d…p±p//Š?{•–ê}œ‡ »è4ûí~ÿÐ6U;:´8…Ìu!ñ=T·!ÕY g ˜x[M¶ËÙb«7 ž\è&ÕøP)ŒÃ±m}£‚¥ysð®¼Ï9Æî’ižÉw¿:`p†¸¨Þ¹’˜èIÖk8`Ö@íCƒphe0Ôl»oÎëò泯•FŠ†|?1 ¦Ö×LÎ „tLÌFÛqVûµáêi[¦6=]Ý:†ŒMªJ;'G£tÔ♳ËÂI$Ýp™æ·*«6ó BÄøÞÔXt±ÛFúe‰q<G&ÔÌS¦2ݶÜޘѳŽ%åfÙbªlChâ8Én>Q³[0HVœqÌDmŽÞ8² Æw+VÔúÊμS/‡Nmciã‡1Îßæõ­Ðûb_zë¹Ú̼’O/šãŠ$ÛO#0JíŠÄH‡K¯‡aÅ!Ë&m­'8’öݪðíä+4ñ{éTó^Â¥WœÏxÑ¥š=Æ8ö­Ëv6]¸–Æü‘~¹òÎm{¼¨®mÍ­Ì_OWÇ›ä9ßï—²²¥H{2a'jKðóžßtÞßKåå4?V÷’yÞ/µ%`Ün‘å‹/*6ãŠÏw“§xÞºkn»ž™gÛ3Zxô‚%ÝûQæô‰FÑQmÚ•mœòœX\iK­‹c©Û jùÆÈwäéSÝT;íþcu(òAèš ¾´ßJü^R·òàêê­ì} æëÒcâý;qíöO@íêÒŠ!DH‹ØÏÇ”qÙcÝtàwž!ã;¦ó¾›®L<÷Ëxk‰–Bgky ¶QKÒs—:”2c~…7 ²›¹³‘»<äÕM Ž(yCCzÓà3]·L„#/o2ÉJy¬oÐÆ#,ôs0¤Ó>6¹>/žÔüañÔÔ#0#,m¢‚ì…BøˆK¼0°Ü»@)-]8—„0‰¸Í3ÕØ™À!ú"Pwü×r.:y`ăx^’‹,I“ZcM‹°ËBz×Q G4¡M ÛFyu &v:šöàs(°.N ac†ÑNŒTÎ9$aJ7õúö˜\¶ nØØC' 8á¹:>“öáëÄ-–4"P½0æ6-rìÜSp¶Ã]ØeцÀË-—‚ìIÒ‹»l!†#00q”:‡íT)¼€¥ê §!#,¢\,"B" $G–¦øhŽFMºxW^Þ¼qË;%·3™BԳá #0Î(çÀÃ-ÒIÏ!®£A26`ƒa™Å„#,¹ã,e¥÷mɬb+"guC+¶‰®&OláEuU¯š2 ²(¡PÆȺËÓììØöÒ¼þù9vl:8Cû´Ú}Òpó-ËTãc­ÓBs$b ƒÜŠŸ‰ ¨—k÷Ä! v×[68Ì©ßQ©t¸}Ðíâ}f£OÕ¶êý•ç@­mÌ¢Gh´®ª&PâÜ©oi0%ßnúš0âÙt[Ox> º7VµÛ¦6)ˆ0Ûƒ{özl(#,Ä?CožN‹kJôNS3‘ö9WÏ=$%CÖÌC[ G¸qÝm³ï]#,ïÇ÷–‹„ds-ÅMVòªw®í’¢Þ0’‡×C‚Z–3oQŠ$ŒÕ©`ãˉK`VZŠè§‡À¡™ç~%‰@½wÙiVkQ-29¾ysæâtÍÅh«†R±Ÿ«–”êâ ˜ÉNtÑÑì›íÓb»6ã–Üyß[á1†+ú-ûâPÝ›$Ë®9˜Kª3lÚY5‚ì[Ô6¾%í^ÛéÔp_îÁ½ãÞ?.y_fêY€òÝûÛžRû®×Þ5s iÖ¸¯)t·Œ©j&cÇxÌ™;%·’Þª|Ü17—”ŽûxÁ{ið#Ü'Šfüo˜ÌÝKŽ6ÁdxAô•µj…q“ÄÊŒ4{¾+ÕÁË,JX!zšZ±-4#0n7ÓIè.®q¬a­H¤óD³íÇLºW2‹$¤¼F—px3„੩Ɉ ÄšÁPæG™Œ†‹Ö]ÑçFO~æO79?QÛ2[ø²:¢zö€¶ä¸Âa#07Öœm—≰6'g=JQÕYqp‚Pg>FC"…*ÒWƒÈð(šZOÅUî]j§'<°ä‡¢î§`öëº=¥#0PcžAä1íí[*‘ªº…CC^#0<‹iõq3¤:dYí¥ZMƒsÊWpÔJ(†Y¸ ÐˆX ŽÐ*AN¤µ‘ÚqXŠôè¼1;éÜv Âh2A¨`¾ä]0ĹŽï9î#0†4奴¾¼lŽ–v¨aQ¨#0xxŸÀ§OP!»Y˜|¬Š\Sض‘<,G@Ao««Q׃5¾ÿ¬+#/ó‘r}4Ó1.ªxƒD@¢ÞlY«}=Å”>gÏh 8Œ#/ž°õ¯alHd{RÑ'W|ÊÀÇ…¶úDÛÕ‡¹ÈâN]~'ƶ¸¢‘U"GÎå±gv#Wìæý’½«X­Þ/jÍç$Fzô/AÈæiRÅQ…&F*ÃÁ¥|kÅ×&¹8u]Û;­Þõy½ñ¶õUűn\—u¹µ%å0³D,Þ*… ”E_T–Û­âÔ[sj¶¹¨P¨¦ù¡Œ,ª i !€¨˜(* Šˆ‚¨£!R€ƒÚzûkâqÛ¡_M…(‘Àu<™ÙØÕ®µ{¤èF,g9¨¢‰æ4¡†[%²S%’Új«_®ÖŠ¶ŠÚ®bI6Æ£B‰ê×ö9·m+ \«x>èf¾—XñËe£ ¥¿ÉÞ*Æ1ƒF(©QL¡ÎùáF–†àA¦í¥ \1Ò?ë~™ƒf–Éà£i”¾iUÉ7b2õšq† V`™ã•Õ¶ R–sã˜òh%Š „… Ñ]R¦EF#‡Ë=HïXjµvDF'`žT‡”¦-&n¦ôRW„Q¤Úm*Ô‘K rªÆÄÆ1¢AJÞb†Zjdh˵…KwL¥ÒHU’ôúË`~+Forrn¨)dÀL$Ô&iÁ‰e—vãÕ•ºÃQ7#,£Œe{èái£¯íe›ÜŒÔÑFš!!¨«XôÖµ¬2#/ÉŒÇqäp¹ròq‹ŒÕ²·Ã+4IQ#0mئ•P¸s@ªÓiåÊ:ˆ&ÚƒVpDVÃ$Îe¯Êî«FúÌAhsS–Q—„l†-A’´Ÿ]><®ÔÝ.›¶NrdF=Ù¦f‚0ÓŒº€,€4C¤#L¤Q!͆–U7«™_ Ò²d¥k .õ±ã#Ž.¥p`Î4Ý€ÞéÇŸh­sb‘'`›’Â7N^mâìÍ ,F¡ö¦ÊDä0x/V†5ßPW…ÖrÕº¡¢ig'D2V0mBFᔪ#0”*à ÖâSC¤Á´Á±‰¦Z6õ¸¶òd²ÊíëÎÅ›[ ¹tøÐA˜Á•£cP¨­tx›I«ƒ«„5±£Tj+ÍŽé» ¦2X6R˜`oeÛûÎF‹Ã‚V²÷Ø;Ú#0#/Dtaý»ˆÓ’?/EFúJ÷ÙÒ&¶µ¶ÆÎӬǰ¤‰±·¼°j°dA¥i«ÊД]Ñe…±BÛ@â2ëF3BÒÑàSÁ„í¬­ƒš”Õ*¥Ggm¾%‹Œg ™R‡…eU[HAÌ Eý‡#0<AZ…LÌ}@eb,hQêE®ú-xݔТïÞš q†àÊtÇ2œAø9­ºÐøkoK fR˜dÔ€Æc8ðM´h¸³#0ÕÍÃ, ‘¦x;„ŒçºWk!®§¨eÃ&ˆ#BÌðM•ƒ™•UÓ3‚-:Ö„D`º#0†#q©½(iZ¤q`8 qC¡ÜKz™dÛCìÁ– íÁ‡Èù…!W›Ì˜ŸEßä %î^sO§âàûZ©L"#0D`¥K.è60bmljŒù‡n¿ïˆW¶¥#,Æ"#"2‰ä/¥`B Pâ”4•¤$‚y×?‰Á( '“ëi?1„‡t#0UNê±Ç¼dQ¦«´þ'b16ÛO hçïKæ˜~¥çe~õ:I3ÌëLo-D’{N°¯h§LúT4úûCt´k-Â#/Èééìðç™;ÀÓÃ}`x“ww_~úgf*1+Úrqfâo.1Áù¼¿R=/ï±´”)v©\\¦gÃŒÞçžÜ o Úc“¹0ˆÁ.;·fÍÝϽ_²0’Ç\èÕáÀyšžp8Lhlˆë“EVomÕÁ(œüÙ„?)ßÓÒšœ8<¼:¾ºŠ–Å tò#,ÃR½˜­»6ÒInªe5e$hCbAªÒI¡"èfÿ×1OdGókÙ=½µcdá÷@dÚ¥EM&¶° ²Š&J‚¦’©•³LšÆ²j+V4”m‰6“c6TXÓMšiP²M$¢”¢TE SPj%"XÓE3FÉ°Ò©¡ik 6aV*"1`B(°’ÎcÄùŠ÷9$!ËÖq:¨b/®%¸Ð{z'c9n>é¹]Öæb{w#,¿âèlüFÆ€‡#,šo§ÑÛmy½6)'Â][A<Ã0 ²¡d‘ ª'¸‚[*¢_A ‘ª ²šôíH—u#/aŒÉH)#/“_G×錦?Š}O!&LÀÁ!„Ù·püLC¸®gNÞ´0Í.EDÄ– ùø†ÛÉ$…Ô:#/6^þG¿ÑæÌ}û:áÏÐÄ1UC)C•´vDûóvôžÍÍôM4(ÕÒúY#,˜6ü#,ò9ÅRFŠÈ”F©6ÑGè-ZæÒ-†š­ˆÏºí\b¬#,…ŠJ¤}Id, ËŸ@`'£9¨Rˆ‰(I#0>ß^apk*ÊÓû)pÇ¿‘pl¡2"?|(µ#/3†çæË£UŒ!òqtxÔ:TQÁ–ŠjB9IˆŠo$ cÓÔå +#/4­ 6<ËRXÒ+H4{˜F›¦øy4vtùóµæQŒ¥:T4¸ŒŠb2Ù)¿mɌУtff0‰XâÒ¡cc#0:@¡…B›P•äèuQ®8âôžŠ ˆT~w ‹’‡$ñP6L1RHtÅ®½—¿gâºóÞ6½mv3£ÖDŽ#/\þÝ9VÙû&Äs¯¼ÜŒR©dXzÈh[ЀòhG£Ò¢L“¸ @ç1$PP†æj©g©–A¶(*¶ˆ! `3!ì6%¡~ÀˆÁUY!º!#,>~Ü× ggšb¥*…$òXH*×£±°žwîÂÒd;¬߶Ù8îî03ŒýiQ<ò†Ìñ½a¿ß…ýo׉1t…Nþ»dÜòòV áÜ=¡ñÌ$GPÁH¹åƒA<dˆÔ”BÍĉ†´kQ3óGœ=ê¿cÓ§µ92Dæö¢Ðñørðþ'„ªô\¦%]»MõöùúM$„…XŒ;¸é#8V Ìô‡DüWÞQwáLB¦¥„tyÆÕ¶ŽÁlÆXÀ™¯âdyû¨âQùÕI!ò*RœZ#K3T&¦1¶™2¡± ±I¢#,Ô¨bîˆãÉvW‚¼­9:Z}^“×`Ò'OˆpóÅÕ#/k朓•®J4óêFEªèÀÆm{I " ‹ÞsØXvc»;­Ë²LÏGÆM˨ꉜ>“-¾J\Â;Ò‚„ª9“^ ›ó0l8Ü`ð,ôMBÝåö8¡¤ÍžŽj'icµbkvPjCQ¡kôüóÝØ:U&£“3åËï„À|¶¾Zü†ý¿Ú¿„ÖV[d’/?(ô/mRŽâb_WéÖî0cEQÂÊîCLϵ}Døf?ă@Í“òWJþ.èœhε,…WMÆ,ÇÙS;?%äyCt¬=0x™1ŠÃOië#/éÛUÒÝ€xw¶#/éO¿ñµü±Ühl2nã#/oMõ⯤Áë›_wC##‡%íùMN8Ó¯2FrÂ#/š“Öb“.‘+Ìèå*F‘£7_›¾qxáR#/xyp°Ï‹8)O ÔU[ÛÆ3YÏ]/}²‚`llÃDAÁ¼HÒU£!#/€&vÄfDÁÄ@A"j!º«ä)_å¥-ƒ+YÞ–Ï(fÓXkZdÓC|²Ì— Ë*aÉ9ŠË'8d±†í1]¥kÎå°˜V&„#,ÆÀ°ô†Î &©³¿”\ú±5,lÁAqªPÁ—KP7îÞh`ÎÌ ÒÎÌp,Ë L6M륕ÉäÀÙÁ5ˆÓ„2¢˜D#/#0r;»#,¸F@0)‘‰š¡ˆm±J@ÐÍL¼¤{ņð4fÒJ‚[Èm£®5êâÕQ¤b%â‘ ©´"؆ùÑÐ\Öô²"DR$[#0á·õÉÇc[ Ï bdHE+²#0o¤Æ3šP´Y&÷f€ ^ Á”j£ÏËüÇ­öég&ü2*Ú 8#/1ê ÄÆ™¥åÇZ³Y #;°tÎ*WšÎÙ¿×ÿ,œ­¸€Ç«¯¦&Ò.À&XpIyënÊLq‡É^›÷9)‚X{OPà»N’Š#/Ù­ŠRèzLË3óË* ,s‘š`ažtÄ¢P…(Åx¥ëIÐdék¬X$€ŒbkJ8A³lÔL+¢ª „„yEÉ4ðï*|¦«‹ ñrMMÅñŒä¹—#0„"&Ëi6ÞöuÍiMíÇT›otÃMˆ\ͱ ùE8þÊU(,¼s§ÃH²±¾U× Ýæ4«#0Öì:1i€$3œŒáÕ„Œ½Fa|­pMk¹uœDu¼¤S­9;.½:b#/"™H‡'‡I³wŒ1¥—ÄlÉ“ètHë6³cZYå’í¢ÝŠá1 ë‡ ´«§$l‚ueI¶ÜŠUf&o±‡f\7M2ÃX=uÑu¾!BÖ¦ yk.¥5)X‚.]7õêH¶BE÷wʼnžŽ3ŽÛ‘#0º¼ ufý¢µo¶&˜ZÑx)FÔÔ|FãÃuná…&LŽÞ¤¶f,%×"kΠ«8c÷½æºuÑjhˆCľÌ,ÛSuSà8W‹™Ù4Žé·r°ý£PX²#0ÐÕ§„%=-Úq;I4²!Äs×HC2T¨âÈJkåÍÝ°ºžÕaÉ–g‡©sv¦&ì¥Çœõë#W'ºM&MÅb)”j7f$m“6CŠvÜÇ<š–ùtÀÒ¥ÅÌëg€qÅŒ«¦n—Y“ò#a‡^ƒ)ÔÃ]­0R/Âo ·Ù—1p-#·#0v( zMT‚L±0K”S#/Κm$Žvš¤ˆ°¡#0ŒIðX§#/N~ض4·8 ÈWz5ˆª#/ÒÒEÑ]$rç|uk*Édñ ñt%Í!r·(çŒCkˆƒ]j$$˜<ŽýÿRßl±§4ûŒlÔ>ý.ŠMBì'˜ªª¸Õ¾;Ç“f€—O·  Vûq†!DºZ:ÙÁntQÓ6‘Â$üïøyJ!í²æ\6E.@ZòXšh??®N:VÞvßT€¤„@ 4SW¼ïωXèÚ°ÍÂ3¿u®qÍ5ídeæJ¸½_B2ßM¢0¥N#/ ÂÒ X{<ÛFó~BÀ™aud¤Ö †l”Åšw–“ã¦âBZß‚3®R¡Z6)¡øf_—;£AËQ1 €Û0UØC˜¡*tžOÏii}ƒ%cP’"6ètà.{Äâ“3™%ÞåÛ³·UjÐ^0JNüÊv#W·hÀ!glÖªÉì(#&B†£ B˜Z'"¬®¢hÊs;@s° vh¦›¦‚Ë.ôŠ¶¾½f­å2CÉœo"/9%‘â”ë“à-MJjsÎG Å{˨˜#/ï¹@áƒ:¿'(ÆÐ4¦øÌa3"ádzJcMös qí”3C°ZèM -<"=1vy®*߀FÉ;ѾΩ††Í‹ŠS“qßbf$’ d$lâ\+H“p3Ç©7Õ$m13”p†Üƒ¤H÷0Æ*šxÐQ†’®*”6MdÔî“Û¥²Ä8:òMhœ¿’ÈÕ€æÌ`Ž›³0ìnð–bEÌÆìZ aH¬NQ™q0º\˜X°#/¹ŒšÉ¤ài)ŒM†TÀÉ(³e’™ÄÜ4Q³3,TT –]Ï7Òu*ö´Ï„mÔZòÚëŠ!‹ŽÖ•\u:]¨Ê4,¨6¨§d15°l&d1™¥!™©@—c& ¤0è”;x¨ÈÉÓ$"àèšØŽ©A —.;#,³a€íhªQ#/ŽDÔ30X op,Ž…%1#/ˆH;”Cq Aƒ9ǺuŒ™IG!›¡,J(sˆHT]S%BÂâI„KHXcX" 'Èž^}'ÀÇ­¸9EA¥¢¢"€ÇѣÜ$Þµ÷x/¤laíä@‡Xhˆ°‰ 6#,ˆTT ‚þÿQo-o`ñ:ŽMao5È:¡~nÚfLCïô™ÞLÏœß i[Êý£¹2aÜÃoPä†ýY{iJ.Z6¯†?j\£¿öJ\ÐMöÙ,0aÃmTø6ÓÁ›BÙßõ¢“õ{ýb]xë1Éî°cùH&èÈå.‰=t"È#À†ú ýUté‰RŠ)Mj‘áxN1׆v1>Yó†8ƒH¸©AÜÔLŽòå8r¹2Å"hñ•¶ìG饾¤5¬¼­ó‘4É/ÆÙQh\Ù€:ibyGò'·ê2caô“œæ”]#,´à V)sKë›–†è+ѸÜíMaö=ju³µÞfàªÕ»º´¢ð:‘GÒ¾šÿL}#0€‘QõÑÞ|Tè8aºDƹ¯¦±ìCÛRÂO²)¶Ên1qè=ù⼂#0á!œ4„ªFNp°Z‘ƒPlr'D’töÒ¡ø›Ì`Tí%‘1,”n2DŠ‡!‘(×’jwÓy»¨•":òÁ1±ØBÉ#0A‘á@BˆD#0‹eY¡##,…Ÿ}#$ÀúÑð[k«o–<wq˾Óc˜Me™1L˜"2m °#,ëŠBbŽ“ÍÔuö>¾ß#,žÉyݦÍÈÒ%ª œC@)³êiŒýJ㈲p4þ©Ñ*$JR1RÓ¹™™!˜"¢´Dƒ@å0²çXL.b²)“_&Ešéâ%ò„óó ¨. ‚›Øƒ‡¯;ÞØî9Ój©<.së#/(ìT`éOe²«ÌÌ…ýž5üå„ü<Ç¡œ?P@z/†I¸hƒ?sxèÑ9g\‘ßÝrõ‚üú5®L…Æ&\³ŒË}©©÷2¦dÛ”dÝË(`"p„è¥=±èTKôl¹à¼RBU­Â¸GSÊ­'¯ÉÔ~ŒSFU¶"ÅÆP—ë†fEb©¬DôÐR?y¶¾C^”`amí0>2˜@“ש"'¶Ðê)(Õ–Õ¥ª‰/Ñ•’Ú©5Fµ¾&¶MZ÷ÛvAäKJŠB{aeZ‰ ¥’ Ý"81‰b15{"´'«˜ÑT †3üð#,Ò &f½¹VßFWè5÷}^ß}­bÒÍ&ŒcXÐB"i{÷÷w‡CçS5Vº™Ã €?B0b"‚) lú0Û·Ú_u6#0Ï>A°\Ú0ŒßôõóI{oéé©ï$±z/|0À(FŠ2BW`@2`ÉPÒ!A¨àL–$’ä ØT„QŠŽÔH™q6' À"­"46”2Z„¡‚FDBÌB „³B¨È 9 ÔoÒèãŒÀ$#/#0ŽÍ1C!Wp…¥#/nbY€ägl¯_b‚v˜ö<ÏGšX`BL+îÀø~£è¡Öi5˜†äͪAªÕ½rp­œÃ¼œüƒËÂXy“p8‰$0#b’$`#,Ü׳–ÄKÓRaÄ£ìjYó*,ßC8¨º!"€£­B™%Fi‰fûEZZ Û'—ÐwAu­uÎík BÀ(x ÅÈLHÊòþÏÝ]}œîL`§·€~V¨b°Hr”JÞ0ªÓ!FlÔ°(ΓJªPÄXÆ»ˆ¨ÓÑj H ’4–ÛR÷Y"p?\™Êᶈk¬§c¾:eOQóõƒì ª› "{ ‚A¦š µ*ƒ ·Â6÷ô¹%÷¶>9u'Ôy«îÜ‘‹d-jÖùü¾Þ†•›Z4ÊË5$©¦¢c†PVÖÌ6¡lÖ”U©-ý*ˆKB…H B‰Ît:0äû%8ú2bº+¹7Ýo|ÍÐO;¢Ü|#,·O1EÃÉ`õ;µãƒXl‚H²íͯ‰§£%;²è½Šnê´Nõ=¶ê ¡ä à΄’??~þ0k:[énìƒÑ…³ü'0ôDѪ‚#0âo}7Ö=u¬¢õ'^„Ö´0㈡4Êv&vái&›ºNî(2@Ž2Â[¼–6PjâÊk­@æ“dÂI¦´[·ÂF#/ÂL²Ùn±…5°|ŽÜq³!´˜Ø2ãBÒgR؃`˜±ÚóOFW%$™C¬éjQ V²ûT#/¹"’#, ²ÒÄ—P.@æ†fe–ͱÛå7À¾r@ˆÇ«º½8›"ˆáYƨèÙ‹ŠšÄ@9{7µºÔ™07ê!ÃäÿŤNîÛ!ÔoÒ$‘ ®¿;Ì %„^â»Öwt¡JU®ßŠ‚\Aô, „'N]ÆþÂ>\ý#/ØÀi/JÕÕž‚b˜ÀzA˜ `D`C® #, Tw=qêå‚p÷ž6á«ÞQ#,+˼ëð< alTðÈÕC‘ãQœVh¦Q+âuóîïÁ"Aº(]¦Xk¾çÏmþ~–+vQ—f3î§Lé#0/ÜÖQHV„Ö½îi÷”j»Ýñíõp¬Â„w"Fî>¡Š/hÙÈ*Ú*ÍÛo÷‘jzIQš~Õöàû÷ƒ'ÎN CÞȃm ×áQ‹ ½Ç÷PÑÒîcšùûŠ¨øq¡O¢&Ò ŽC²(£½_ïˆôØ.Øbuñ؈3^_zŠ0„ÉŒÉç]EEd—vº)"ÃL¯¿ÞdE~ôç¾^y{ÜVpÕ(¦ÈZ1F%y7Y¼“¨¬!ŒF库jfÚÝ„_Ì¥##0`!úR)¨EPÚÇ-Û~Ÿqáã( £×¹O ñ0>g¨< +M ¦#ç6|¯¤#0z•:zï¬$Y‘£(ÂAÄ í:Ì1H(È #b¦V‹%–d¶lÚ¿'çŠú¯ãï¼ih†6Tm%FÒ”ÒÚ¿«ü]ÞhL¾kØŸ2*…DLþÈJZÛì™t!u<ÄFÉi ΄IØ?;“Q0(¤eQNȦÆ*&Ö[E¢ŠŒÛ3Í{çž[RjÒÓhNº-»Ä(?š…úCgý/G§¸ž÷Ïôÿ ƒ³I⯈A6l#/Ãè<¾zÔ×êÈ©¥ÈYìÖá 1>A‹{by"À‡`i€p@Êr2Êaˆ’éÅ>—5EWªÿn1ab)$Iͤú#0¡õQàgHb&7 (ÛhÁpýP7'-=6#0ŠîˆZ³#/0ˆÀüÙ(ú¬‰ˆ¡Í=€TE_b P#0Ÿ¾É Þmh(‘ !¥FˆA#,Š‘¥&©m:œêúnÞSÛ^ßkm»‰€#,‡ôIv(Vè68+MGØ‹#,øÔ*OWO?AËƨg–5CH~íµï°áý¦Ã+ÓšXm¦= άâZR6Ÿš„á#”~¤<ÚCi´Pçk+\[iJ8çÌP?vtÆïs¿}ë6Øeú0ÝEŒLûÌÂécÜ>ñô~àä*×Wëm}z~^7dѪY¦&ÉF£5(¤ ¨¶2j¡M£[ñ­x‰P£j(ÚJeaRÛñþCj T U#0#ï!‚ŒB}Ó&ì$b!T²€„a¤J=ÔT¡Û¯™ÂÄ°¥Û°ÎøOª;×n²Ðw]ÌR#,¿Ú"«WšÑmF+AjPª4&ÔÙb*Å­M-e_±×éÛ㟧¡¿ÎÎíí¥VY) iÏ×Ò©ô¡L ¦¿¦q¦¾Œf µc@#J̤Íø““$ßæ Ã'À}<†ì-¤¤»¢ÎB-­YÚ#J’F‡F†3´ŒgöàÀl(¢I¼´²P(e´O’nÓ`Úh¥M{›#/r–Û]Ý•ã^o+]Sh²kz«’¢Þnë3vfUuÍÛ[PÍdYjòîÆh›]Ý]Ý­&Ê’¦D¦Æ·Õ¼Ó^uuy×vÊœ« R¢#/F*6Û(Á#/2,E´¦–É©2ʹZêÞ]]yçko‹e™V[)kÉ·Wvë,f¬ºk¤ÜÔêɶ Žáª-Ré H…„Ù‚ýQÏl“PæI8hr&Õ‰A?Ã4¤&#0_:3Ú T¶®ýO›Í}4g3 tR¡ùRa1T,½ø˜1©›Àá€!w¼*Ò£úµ601yçd¹ö¢ëþ£Ž_·#,× .ÂIÐG·ÃbzpOK7¢°ŠBºov1&òy›i8°-¶p §fe°æôêI¶áSŽ[¾'ĵ‹Ô#0¦$l²Æ¦÷Ò·N4À¦¦ssÆú¼ŒÌ=rêÏ«o8‚EW(¤g"ð¸7×°˜Þ9¢¿Uì!$Ôè"F\ef^ͱ|3¡w~E‡»)%þ":3 Mäÿ¸ZÖ:>É+HéPyoÇXO©#/!‰åÄT,¶µFPÓ#? Ö±ÛU›Ôç¾Iêd!il…3$Æeðª|g¨Ü5J>ë7í:•#/ñÜpÅ`×L‹áòÒ´ód›¢>Ìëw¤O¬àÁÛ }й(R5œ,#)7˜ÊbÒ¥ ¦Âàs Ym¦fÇÖBl#, ÝE³F]#,¡ îL³>nþÇÈœJº›%ÀE‘/HàC²Æó‹e‰Dh¥§º³J»ê7Ó€îŒaú Xbå 8=‰pâµÑà/ðùI(¿DS‰Bí¹¡ñàËdAÛÝIeÊ<‰£t ý´­I,G#/š½¾FY)a1æ+Òt`fA°)¥KâÅm¹^„æuWÊ­mæÚƒp8QC'øhY‹jr•Ò‚kă#t[Z…];S‡G~ºÜØáÂ\ a9Q¾§S>Ò¨roaÊ!"0ŽCTö /m 6‹' — Hs<Ÿ’¸™œðÍC2Ôå¥Ç5À8eøryŒ0îI¶Á!#ÚqoÈS‰ÒodCŠà{!g#°ñöfb@ÞäMO\5Ê¿g’ÿÑzúw7CðÄ4#,  Z ý9{ñ[Šp+ÊŠCp'V,œþ’ÈD?À¥#° <'’=D|.~ ~~í¶É㆚^ù—rb¡1ýUm#,ŸôôÿñüõÇÿýOöäþŸòÿ‡ÿOýßïüÿíÿßþþoúrÇòþ'ÿ-ÿòÿû·á_üÿþwôüÿŸNŸÿßñÿÃÿßÜ?á÷ýøåÿ~¿ûÿåÿËþ÷Óû¿Õýßòûµÿo¿ýßôÿŸ_Î6ËþŸW×÷ý±ûT틇7Œs#,‚#,¼!œÈ«‡÷ÿÈHˆm%M¤ÁêúØHUÙ2ùz¨¨C™IWÿHþèþ¿¹ÝØ’@“3?·×l¶¿E²%Ë=™ yµÚ]šm×'$,ÆšÈÂlp?¦ÎÿöÞÊa,€cþ{÷íLsÚP\ÒÞ%¹¼Cu¸€ux˜½ÑåZ£#ÜÎ<Šð’ H!Яنcž1cÔ½€V#,5#0ŸêP¦wk|Ï?ËìdÏ3?L¿—jâu+Sb9u(ðÔ˜Ìi¬d›ÅAb¡½ì{ LBH|uû0?àÍUÝÊrI@í{öõ?ùµÙòÎ$|e­1Ìv±•‘ÊÙ™þ‡``Ú•5$Cc#^Í­1±­êh$qƆËH‡® a†Ïù0hÔ!2"ªÔ«ëM"eà;áBdìîµ»“ŒhiyË#0e,¶K Ô=Í<ÐÓt¨£Ì᜺øq›ÔÛTÐâc2,aÅÙ$B ¶º@“0âñ'ì–l,—rF k†Þ±êûiÍ⑳»“Ή%¥é¨C Ê0·—º!žóN¡ì†ƒ/ž™Œ£þšfÂlüë 6P®*f,‚Z"fVHÄb×/;bòÙ#,c.Їí|mÄÎTcæQZ§ðHuo ‘Øáä36lȵétª§A…Ëå—CÖÿʬW¯ýÔƒTö$\ž<ö|г^mæªb»’U˜'4m6&>4“Ĉ˜y†}˜K¸RNEÁBxR²àþÞo~çÃnc^ TZJ”RèåÓ‰]nÄ«ÔÇŽc9™¨" ªž(‚Ñ<í׉n6, ¬Ù 2CpÜ©#/\”3‚”±QS¸YbyÒ4_¥„ « ƒ * §‘Öt(wä¸Ë(D2»Ø@õú/k¥ï|“wù`qÒhfê°sÅÞcÑgy´F#0èÙJS³S~#0ÞO"2˜"‚#0ŒjÅĨ”5QjÊS+lZ’Ô¦’E† „‹R”)ArÊNq#,Š'vgyô‰#/˜Ú0?ØÉ1áTÝo0PE-yàPKµ)+CÙÏÞ#,Ÿ@pÍaþ¢P‰ ÞÛ•Y#,ïIÈ;‰€€ÿkD9Ϙ­?¶Y2IÔCÄý::9ÿ®o&Ø-1ÄÄ%G…ªlÑ•t›?ûoòØh¯¬Lx$Ù-áŽE¾°èèN¶¦ÍÞõ%¾DµÖ#,pŽ`kS•šleÌ®û¡íú]ýd`ÝÒ“lUÍ  i¥SG†fùz1$Ø÷„ùQaNÃ,2U1lííÅÁIè¼e¯¦›×äl¶³eÃ,­øÇW’@Qb“Vƒ*0Y?E¯V+s\ÚM£èÛrÔÒ£ƶõF¯…k{˜ÆÕ&$H*’M##,¦I2ÈY­Øš´½áT,K°â‡'"Œ2HhiúéOî/FîS„d‘#,ÜbèDêv;laðTH"d@l‹ã߸Þt׸|1õ“«jFz)hÜÄO?ý×n¸‹˜œ(4’,€HÍãà)_õžÃéî£Í“ÐŽÔs}\EðâQ‚ú˜pó/Ãbƒÿ® °(mv£¸ìÐwGm\ÙkY*Ó&ºkvÛu–¯¾Ñ6jM{}š­~º(¬Z Eíõ{=Ù?Hþ¾j'báÓåÊwøJ=Þ¹Ž<Œmµ ~.«tKD8èèÅ<¢|%ˆ2ŠO¯ïÅÿ#0Ÿóìûàí&f˜HÁ?øxÉB-à6àyiûÔU'ÿŽ¿ù4j† &0žß÷¶ýBÜ?ËUÜ#/™Å}Á-ZÿïêÿwÞÙ¼‹ñxzþ7_Ÿ~dãW>;ÈqÏNˆñ{Çeý#Çàáíá =‡Œ°·ËçðàxÉá‘ÏÀò¯ýûO§ÈëÿÚcòÿ §ì…ãŒB?ü4»â~Yx”Z?¶,ïgÿšÌµ{åÓ…¥Ô+¨›4ÃÒ#¯á ªßàÌOé Ö•.$ñ‚([ù*Qÿ®‹YÀé]¼N*ˆ¦pAöj¸\eœÁ©‡|Ž!ñõÚtí¢tYXþ^rÄÞ¸bòØKððŠ‰kÊÚs sÇÙÏ,ÀŠÃ$ÉßNñõ]aûQDa›áùþ? ¦Ÿw93êwÕ±íÓãÿ5dxóŒâÅ#/¾“©j¬b/¼<xOí§þÑ#0“Ò=Â"¯“å#,Aÿü]ÉáBBuÅÁ
+#<==
diff --git a/doc/ws_main.py b/doc/ws_main.py
index 237ea743..a88452b3 100755
--- a/doc/ws_main.py
+++ b/doc/ws_main.py
@@ -250,6 +250,8 @@ def scansize(self):
def options(opt):
opt.add_option('--exe', action='store_true', default=False, help='Execute the program after it is compiled')
+ opt.add_option('--performance', action='store_true', help='Build a performance report based on google analytics')
+ opt.add_option('--performance-detailed',action='store_true',help='print detailed test results (date,time, build id and results) to csv file named _detailed_table.csv.')
def configure(conf):
search_path = '~/.local/bin /usr/local/bin/ /usr/bin'
@@ -291,7 +293,7 @@ TOC_HEAD = """
<!-- load the theme CSS file -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" rel="stylesheet"/>
- <link href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" rel="stylesheet" />
+ <link href="https://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" rel="stylesheet" />
<!-- include the jQuery library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js">
@@ -887,8 +889,15 @@ def build_cp(bld,dir,root,callback):
-
-
+def create_analytic_report(task):
+ try:
+ import AnalyticsWebReport as analytics
+ if task.generator.bld.options.performance_detailed:
+ analytics.main(verbose = Logs.verbose,detailed_test_stats='yes')
+ else:
+ analytics.main(verbose = Logs.verbose)
+ except Exception as e:
+ raise Exception('Error importing or using AnalyticsWebReport script: %s' % e)
@@ -916,6 +925,12 @@ def build(bld):
bld(rule=my_copy, target=x)
bld.add_group()
+ if bld.options.performance or bld.options.performance_detailed:
+ bld(rule=create_analytic_report)
+ bld.add_group()
+ bld(rule=convert_to_html_toc_book, source='trex_analytics.asciidoc waf.css', target='trex_analytics.html',scan=ascii_doc_scan);
+ return
+
bld(rule=my_copy, target='my_chart.js')
build_cp(bld,'hlt_args.asciidoc','stl/trex_stl_lib', parse_hlt_args)
@@ -973,9 +988,12 @@ def build(bld):
bld(rule=convert_to_html_toc_book,
source='trex_rpc_server_spec.asciidoc waf.css', target='trex_rpc_server_spec.html',scan=ascii_doc_scan);
-
+
bld(rule=convert_to_html_toc_book,
source='trex_scapy_rpc_server.asciidoc waf.css', target='trex_scapy_rpc_server.html',scan=ascii_doc_scan);
+
+ bld(rule=convert_to_html_toc_book,
+ source='trex-analytics-howto.asciidoc waf.css', target='trex-analytics-howto.html',scan=ascii_doc_scan);
bld(rule='${ASCIIDOC} -a stylesheet=${SRC[1].abspath()} -a icons=true -a toc2 -a max-width=55em -o ${TGT} ${SRC[0].abspath()}',
source='vm_doc.asciidoc waf.css', target='vm_doc.html', scan=ascii_doc_scan)
@@ -1062,16 +1080,62 @@ def release(bld):
os.system('cp -rv build/release_notes.* '+ release_dir)
+def rsync_int(bld, src, dst):
+ cmd = 'rsync -av --del --rsh=ssh build/{src} {host}:{dir}/{dst}'.format(
+ src = src,
+ host = Env().get_local_web_server(),
+ dir = Env().get_remote_release_path() + '../doc',
+ dst = dst)
+ ret = os.system(cmd)
+ if ret:
+ bld.fatal("cmd '%s' exited with return status" % (cmd, ret))
+
+
+def rsync_ext(bld, src, dst):
+ cmd = 'rsync -avz --del -e "ssh -i {key}" --rsync-path=/usr/bin/rsync build/{src} {user}@{host}:{dir}/doc/{dst}'.format(
+ key = Env().get_trex_ex_web_key(),
+ src = src,
+ user = Env().get_trex_ex_web_user(),
+ host = Env().get_trex_ex_web_srv(),
+ dir = Env().get_trex_ex_web_path(),
+ dst = dst)
+ ret = os.system(cmd)
+ if ret:
+ bld.fatal("cmd '%s' exited with return status" % (cmd, ret))
+
+
def publish(bld):
- # copy all the files to our web server
- remote_dir = "%s:%s" % ( Env().get_local_web_server(), Env().get_remote_release_path ()+'../doc/')
- os.system('rsync -av --del --rsh=ssh build/ %s' % (remote_dir))
+ # copy all the files to internal web server
+ rsync_int(bld, '', '')
def publish_ext(bld):
- from_ = 'build/'
- os.system('rsync -avz --del -e "ssh -i %s" --rsync-path=/usr/bin/rsync %s %s@%s:%s/doc/' % (Env().get_trex_ex_web_key(),from_, Env().get_trex_ex_web_user(),Env().get_trex_ex_web_srv(),Env().get_trex_ex_web_path() ) )
-
+ # copy all the files to external web server
+ rsync_ext(bld, '', '')
+
+
+def publish_perf(bld):
+ # copy performance files to internal and external servers
+ rsync_int(bld, 'trex_analytics.html', '')
+ rsync_ext(bld, 'trex_analytics.html', '')
+ rsync_int(bld, 'trex_analytics.json', '')
+ rsync_ext(bld, 'trex_analytics.json', '')
+ rsync_int(bld, 'images/*_latest_test_*', 'images/')
+ rsync_ext(bld, 'images/*_latest_test_*', 'images/')
+ rsync_int(bld, 'images/*_trend_graph.*', 'images/')
+ rsync_ext(bld, 'images/*_trend_graph.*', 'images/')
+ rsync_int(bld, 'images/*_trend_stats.*', 'images/')
+ rsync_ext(bld, 'images/*_trend_stats.*', 'images/')
+ rsync_int(bld, 'images/_detailed_table.csv', 'images/')
+ rsync_ext(bld, 'images/_detailed_table.csv', 'images/')
+
+ rsync_int(bld, 'images/_comparison.png', 'images/')
+ rsync_ext(bld, 'images/_comparison.png', 'images/')
+
+ rsync_int(bld, 'images/_comparison_stats_table.csv', 'images/')
+ rsync_ext(bld, 'images/_comparison_stats_table.csv', 'images/')
+
+
def publish_test(bld):
# copy all the files to our web server
diff --git a/doc/wscript b/doc/wscript
index 187a5528..f574e69b 100755
--- a/doc/wscript
+++ b/doc/wscript
@@ -52,6 +52,9 @@ def publish_ext(bld):
def publish_web(bld):
ws_main.publish_web(bld)
+def publish_perf(bld):
+ ws_main.publish_perf(bld)
+
def sync(bld):
ws_main.sync(bld)
diff --git a/external_libs/ibverbs/include/infiniband/arch.h b/external_libs/ibverbs/include/infiniband/arch.h
new file mode 100644
index 00000000..6f419c53
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/arch.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2005 Topspin Communications. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INFINIBAND_ARCH_H
+#define INFINIBAND_ARCH_H
+
+#include <stdint.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#ifdef htonll
+#undef htonll
+#endif
+
+#ifdef ntohll
+#undef ntohll
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline uint64_t htonll(uint64_t x) { return bswap_64(x); }
+static inline uint64_t ntohll(uint64_t x) { return bswap_64(x); }
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint64_t htonll(uint64_t x) { return x; }
+static inline uint64_t ntohll(uint64_t x) { return x; }
+#else
+#error __BYTE_ORDER is neither __LITTLE_ENDIAN nor __BIG_ENDIAN
+#endif
+
+/*
+ * Architecture-specific defines. Currently, an architecture is
+ * required to implement the following operations:
+ *
+ * mb() - memory barrier. No loads or stores may be reordered across
+ * this macro by either the compiler or the CPU.
+ * rmb() - read memory barrier. No loads may be reordered across this
+ * macro by either the compiler or the CPU.
+ * wmb() - write memory barrier. No stores may be reordered across
+ * this macro by either the compiler or the CPU.
+ * wc_wmb() - flush write combine buffers. No write-combined writes
+ * will be reordered across this macro by either the compiler or
+ * the CPU.
+ */
+
+#if defined(__i386__)
+
+#define mb() asm volatile("lock; addl $0,0(%%esp) " ::: "memory")
+#define rmb() mb()
+#define wmb() asm volatile("" ::: "memory")
+#define wc_wmb() mb()
+#define nc_wmb() wmb()
+
+#elif defined(__x86_64__)
+
+#define mb() asm volatile("" ::: "memory")
+#define rmb() mb()
+#define wmb() asm volatile("" ::: "memory")
+#define wc_wmb() asm volatile("sfence" ::: "memory")
+#define nc_wmb() wmb()
+#define WC_AUTO_EVICT_SIZE 64
+
+#elif defined(__PPC64__)
+
+#define mb() asm volatile("sync" ::: "memory")
+#define rmb() asm volatile("lwsync" ::: "memory")
+#define wmb() rmb()
+#define wc_wmb() mb()
+#define nc_wmb() mb()
+#define WC_AUTO_EVICT_SIZE 64
+
+#elif defined(__ia64__)
+
+#define mb() asm volatile("mf" ::: "memory")
+#define rmb() mb()
+#define wmb() mb()
+#define wc_wmb() asm volatile("fwb" ::: "memory")
+#define nc_wmb() wmb()
+
+#elif defined(__PPC__)
+
+#define mb() asm volatile("sync" ::: "memory")
+#define rmb() mb()
+#define wmb() mb()
+#define wc_wmb() wmb()
+#define nc_wmb() wmb()
+
+#elif defined(__sparc_v9__)
+
+#define mb() asm volatile("membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad" ::: "memory")
+#define rmb() asm volatile("membar #LoadLoad" ::: "memory")
+#define wmb() asm volatile("membar #StoreStore" ::: "memory")
+#define wc_wmb() wmb()
+#define nc_wmb() wmb()
+
+#elif defined(__sparc__)
+
+#define mb() asm volatile("" ::: "memory")
+#define rmb() mb()
+#define wmb() mb()
+#define wc_wmb() wmb()
+#define nc_wmb() wmb()
+
+#elif defined(__aarch64__)
+
+/* Perhaps dmb would be sufficient? Let us be conservative for now. */
+#define mb() asm volatile("dsb sy" ::: "memory")
+#define rmb() asm volatile("dsb ld" ::: "memory")
+#define wmb() asm volatile("dsb st" ::: "memory")
+#define wc_wmb() wmb()
+#define nc_wmb() wmb()
+
+#elif defined(__s390x__)
+
+#define mb() asm volatile("" ::: "memory")
+#define rmb() mb()
+#define wmb() mb()
+#define wc_wmb() wmb()
+#define nc_wmb() wmb()
+
+#else
+
+#error No architecture specific memory barrier defines found!
+
+#endif
+
+#ifdef WC_AUTO_EVICT_SIZE
+static inline int wc_auto_evict_size(void) { return WC_AUTO_EVICT_SIZE; };
+#else
+static inline int wc_auto_evict_size(void) { return 0; };
+#endif
+
+#endif /* INFINIBAND_ARCH_H */
diff --git a/external_libs/ibverbs/include/infiniband/driver.h b/external_libs/ibverbs/include/infiniband/driver.h
new file mode 100644
index 00000000..b59bc452
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/driver.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2005, 2006 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2005 PathScale, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INFINIBAND_DRIVER_H
+#define INFINIBAND_DRIVER_H
+
+#include <infiniband/verbs.h>
+#include <infiniband/kern-abi.h>
+
+#ifdef __cplusplus
+# define BEGIN_C_DECLS extern "C" {
+# define END_C_DECLS }
+#else /* !__cplusplus */
+# define BEGIN_C_DECLS
+# define END_C_DECLS
+#endif /* __cplusplus */
+
+/*
+ * Extension that low-level drivers should add to their .so filename
+ * (probably via libtool "-release" option). For example a low-level
+ * driver named "libfoo" should build a plug-in named "libfoo-rdmav2.so".
+ */
+#define IBV_DEVICE_LIBRARY_EXTENSION rdmav2
+
+enum verbs_xrcd_mask {
+ VERBS_XRCD_HANDLE = 1 << 0,
+ VERBS_XRCD_RESERVED = 1 << 1
+};
+
+struct verbs_xrcd {
+ struct ibv_xrcd xrcd;
+ uint32_t comp_mask;
+ uint32_t handle;
+};
+
+enum verbs_srq_mask {
+ VERBS_SRQ_TYPE = 1 << 0,
+ VERBS_SRQ_XRCD = 1 << 1,
+ VERBS_SRQ_CQ = 1 << 2,
+ VERBS_SRQ_NUM = 1 << 3,
+ VERBS_SRQ_RESERVED = 1 << 4
+};
+
+struct verbs_srq {
+ struct ibv_srq srq;
+ uint32_t comp_mask;
+ enum ibv_srq_type srq_type;
+ struct verbs_xrcd *xrcd;
+ struct ibv_cq *cq;
+ uint32_t srq_num;
+};
+
+enum verbs_qp_mask {
+ VERBS_QP_XRCD = 1 << 0,
+ VERBS_QP_RESERVED = 1 << 1
+};
+
+struct verbs_mw {
+ struct ibv_mw mw;
+ uint32_t handle;
+ enum ibv_mw_type type;
+};
+
+struct verbs_qp {
+ struct ibv_qp qp;
+ uint32_t comp_mask;
+ struct verbs_xrcd *xrcd;
+};
+typedef struct ibv_device *(*ibv_driver_init_func)(const char *uverbs_sys_path,
+ int abi_version);
+typedef struct verbs_device *(*verbs_driver_init_func)(const char *uverbs_sys_path,
+ int abi_version);
+
+void ibv_register_driver(const char *name, ibv_driver_init_func init_func);
+void verbs_register_driver(const char *name, verbs_driver_init_func init_func);
+int ibv_cmd_get_context(struct ibv_context *context, struct ibv_get_context *cmd,
+ size_t cmd_size, struct ibv_get_context_resp *resp,
+ size_t resp_size);
+int ibv_cmd_query_device(struct ibv_context *context,
+ struct ibv_device_attr *device_attr,
+ uint64_t *raw_fw_ver,
+ struct ibv_query_device *cmd, size_t cmd_size);
+int ibv_cmd_query_port(struct ibv_context *context, uint8_t port_num,
+ struct ibv_port_attr *port_attr,
+ struct ibv_query_port *cmd, size_t cmd_size);
+int ibv_cmd_query_gid(struct ibv_context *context, uint8_t port_num,
+ int index, union ibv_gid *gid);
+int ibv_cmd_query_pkey(struct ibv_context *context, uint8_t port_num,
+ int index, uint16_t *pkey);
+int ibv_cmd_alloc_pd(struct ibv_context *context, struct ibv_pd *pd,
+ struct ibv_alloc_pd *cmd, size_t cmd_size,
+ struct ibv_alloc_pd_resp *resp, size_t resp_size);
+int ibv_cmd_dealloc_pd(struct ibv_pd *pd);
+int ibv_cmd_open_xrcd(struct ibv_context *context, struct verbs_xrcd *xrcd,
+ int vxrcd_size,
+ struct ibv_xrcd_init_attr *attr,
+ struct ibv_open_xrcd *cmd, size_t cmd_size,
+ struct ibv_open_xrcd_resp *resp,
+ size_t resp_size);
+int ibv_cmd_close_xrcd(struct verbs_xrcd *xrcd);
+#define IBV_CMD_REG_MR_HAS_RESP_PARAMS
+int ibv_cmd_reg_mr(struct ibv_pd *pd, void *addr, size_t length,
+ uint64_t hca_va, int access,
+ struct ibv_mr *mr, struct ibv_reg_mr *cmd,
+ size_t cmd_size,
+ struct ibv_reg_mr_resp *resp, size_t resp_size);
+int ibv_cmd_dereg_mr(struct ibv_mr *mr);
+int ibv_cmd_alloc_mw(struct ibv_pd *pd, enum ibv_mw_type type,
+ struct verbs_mw *mw, struct ibv_alloc_mw *cmd,
+ size_t cmd_size,
+ struct ibv_alloc_mw_resp *resp, size_t resp_size);
+int ibv_cmd_dealloc_mw(struct verbs_mw *mw,
+ struct ibv_dealloc_mw *cmd, size_t cmd_size);
+int ibv_cmd_create_cq(struct ibv_context *context, int cqe,
+ struct ibv_comp_channel *channel,
+ int comp_vector, struct ibv_cq *cq,
+ struct ibv_create_cq *cmd, size_t cmd_size,
+ struct ibv_create_cq_resp *resp, size_t resp_size);
+int ibv_cmd_poll_cq(struct ibv_cq *cq, int ne, struct ibv_wc *wc);
+int ibv_cmd_req_notify_cq(struct ibv_cq *cq, int solicited_only);
+#define IBV_CMD_RESIZE_CQ_HAS_RESP_PARAMS
+int ibv_cmd_resize_cq(struct ibv_cq *cq, int cqe,
+ struct ibv_resize_cq *cmd, size_t cmd_size,
+ struct ibv_resize_cq_resp *resp, size_t resp_size);
+int ibv_cmd_destroy_cq(struct ibv_cq *cq);
+
+int ibv_cmd_create_srq(struct ibv_pd *pd,
+ struct ibv_srq *srq, struct ibv_srq_init_attr *attr,
+ struct ibv_create_srq *cmd, size_t cmd_size,
+ struct ibv_create_srq_resp *resp, size_t resp_size);
+int ibv_cmd_create_srq_ex(struct ibv_context *context,
+ struct verbs_srq *srq, int vsrq_sz,
+ struct ibv_srq_init_attr_ex *attr_ex,
+ struct ibv_create_xsrq *cmd, size_t cmd_size,
+ struct ibv_create_srq_resp *resp, size_t resp_size);
+int ibv_cmd_modify_srq(struct ibv_srq *srq,
+ struct ibv_srq_attr *srq_attr,
+ int srq_attr_mask,
+ struct ibv_modify_srq *cmd, size_t cmd_size);
+int ibv_cmd_query_srq(struct ibv_srq *srq,
+ struct ibv_srq_attr *srq_attr,
+ struct ibv_query_srq *cmd, size_t cmd_size);
+int ibv_cmd_destroy_srq(struct ibv_srq *srq);
+
+int ibv_cmd_create_qp(struct ibv_pd *pd,
+ struct ibv_qp *qp, struct ibv_qp_init_attr *attr,
+ struct ibv_create_qp *cmd, size_t cmd_size,
+ struct ibv_create_qp_resp *resp, size_t resp_size);
+int ibv_cmd_open_qp(struct ibv_context *context,
+ struct verbs_qp *qp, int vqp_sz,
+ struct ibv_qp_open_attr *attr,
+ struct ibv_open_qp *cmd, size_t cmd_size,
+ struct ibv_create_qp_resp *resp, size_t resp_size);
+int ibv_cmd_query_qp(struct ibv_qp *qp, struct ibv_qp_attr *qp_attr,
+ int attr_mask,
+ struct ibv_qp_init_attr *qp_init_attr,
+ struct ibv_query_qp *cmd, size_t cmd_size);
+int ibv_cmd_modify_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr,
+ int attr_mask,
+ struct ibv_modify_qp *cmd, size_t cmd_size);
+int ibv_cmd_destroy_qp(struct ibv_qp *qp);
+int ibv_cmd_post_send(struct ibv_qp *ibqp, struct ibv_send_wr *wr,
+ struct ibv_send_wr **bad_wr);
+int ibv_cmd_post_recv(struct ibv_qp *ibqp, struct ibv_recv_wr *wr,
+ struct ibv_recv_wr **bad_wr);
+int ibv_cmd_post_srq_recv(struct ibv_srq *srq, struct ibv_recv_wr *wr,
+ struct ibv_recv_wr **bad_wr);
+int ibv_cmd_create_ah(struct ibv_pd *pd, struct ibv_ah *ah,
+ struct ibv_ah_attr *attr);
+int ibv_cmd_destroy_ah(struct ibv_ah *ah);
+int ibv_cmd_attach_mcast(struct ibv_qp *qp, const union ibv_gid *gid, uint16_t lid);
+int ibv_cmd_detach_mcast(struct ibv_qp *qp, const union ibv_gid *gid, uint16_t lid);
+
+struct ibv_exp_flow *ibv_exp_cmd_create_flow(struct ibv_qp *qp,
+ struct ibv_exp_flow_attr *flow_attr);
+int ibv_exp_cmd_destroy_flow(struct ibv_exp_flow *flow_id);
+struct ibv_flow *ibv_cmd_create_flow(struct ibv_qp *qp,
+ struct ibv_flow_attr *flow_attr);
+int ibv_cmd_destroy_flow(struct ibv_flow *flow_id);
+
+int ibv_dontfork_range(void *base, size_t size);
+int ibv_dofork_range(void *base, size_t size);
+void ibv_cmd_query_device_assign(struct ibv_device_attr *device_attr,
+ uint64_t *raw_fw_ver,
+ struct ibv_query_device_resp *resp);
+
+/*
+ * sysfs helper functions
+ */
+const char *ibv_get_sysfs_path(void);
+
+int ibv_read_sysfs_file(const char *dir, const char *file,
+ char *buf, size_t size);
+
+static inline int verbs_get_srq_num(struct ibv_srq *srq, uint32_t *srq_num)
+{
+ struct verbs_srq *vsrq = container_of(srq, struct verbs_srq, srq);
+ if (vsrq->comp_mask & VERBS_SRQ_NUM) {
+ *srq_num = vsrq->srq_num;
+ return 0;
+ }
+ return ENOSYS;
+}
+
+#endif /* INFINIBAND_DRIVER_H */
diff --git a/external_libs/ibverbs/include/infiniband/driver_exp.h b/external_libs/ibverbs/include/infiniband/driver_exp.h
new file mode 100644
index 00000000..31e4a5a8
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/driver_exp.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2005, 2006 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2005 PathScale, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INFINIBAND_DRIVER_EXP_H
+#define INFINIBAND_DRIVER_EXP_H
+
+#include <infiniband/verbs_exp.h>
+#include <infiniband/driver.h>
+#include <infiniband/kern-abi_exp.h>
+
+int ibv_exp_cmd_query_device(struct ibv_context *context,
+ struct ibv_exp_device_attr *device_attr,
+ uint64_t *raw_fw_ver,
+ struct ibv_exp_query_device *cmd, size_t cmd_size);
+int ibv_exp_cmd_create_qp(struct ibv_context *context,
+ struct verbs_qp *qp, int vqp_sz,
+ struct ibv_exp_qp_init_attr *attr_exp,
+ void *cmd_buf, size_t lib_cmd_size, size_t drv_cmd_size,
+ void *resp_buf, size_t lib_resp_size, size_t drv_resp_size,
+ int force_exp);
+int ibv_exp_cmd_create_dct(struct ibv_context *context,
+ struct ibv_exp_dct *dct,
+ struct ibv_exp_dct_init_attr *attr,
+ struct ibv_exp_create_dct *cmd,
+ size_t lib_cmd_sz, size_t drv_cmd_sz,
+ struct ibv_exp_create_dct_resp *resp,
+ size_t lib_resp_sz, size_t drv_resp_sz);
+int ibv_exp_cmd_destroy_dct(struct ibv_context *context,
+ struct ibv_exp_dct *dct,
+ struct ibv_exp_destroy_dct *cmd,
+ size_t lib_cmd_sz, size_t drv_cmd_sz,
+ struct ibv_exp_destroy_dct_resp *resp,
+ size_t lib_resp_sz, size_t drv_resp_sz);
+int ibv_exp_cmd_query_dct(struct ibv_context *context,
+ struct ibv_exp_query_dct *cmd,
+ size_t lib_cmd_sz, size_t drv_cmd_sz,
+ struct ibv_exp_query_dct_resp *resp,
+ size_t lib_resp_sz, size_t drv_resp_sz,
+ struct ibv_exp_dct_attr *attr);
+int ibv_exp_cmd_arm_dct(struct ibv_context *context,
+ struct ibv_exp_arm_attr *attr,
+ struct ibv_exp_arm_dct *cmd,
+ size_t lib_cmd_sz, size_t drv_cmd_sz,
+ struct ibv_exp_arm_dct_resp *resp,
+ size_t lib_resp_sz, size_t drv_resp_sz);
+int ibv_exp_cmd_modify_cq(struct ibv_cq *cq,
+ struct ibv_exp_cq_attr *attr,
+ int attr_mask,
+ struct ibv_exp_modify_cq *cmd, size_t cmd_size);
+int ibv_exp_cmd_create_cq(struct ibv_context *context, int cqe,
+ struct ibv_comp_channel *channel,
+ int comp_vector, struct ibv_cq *cq,
+ struct ibv_exp_create_cq *cmd, size_t lib_cmd_sz, size_t drv_cmd_sz,
+ struct ibv_create_cq_resp *resp, size_t lib_resp_sz, size_t drv_resp_sz,
+ struct ibv_exp_cq_init_attr *attr);
+int ibv_exp_cmd_modify_qp(struct ibv_qp *qp, struct ibv_exp_qp_attr *attr,
+ uint64_t attr_mask, struct ibv_exp_modify_qp *cmd,
+ size_t cmd_size);
+int ibv_exp_cmd_create_mr(struct ibv_exp_create_mr_in *in, struct ibv_mr *mr,
+ struct ibv_exp_create_mr *cmd, size_t lib_cmd_sz, size_t drv_cmd_sz,
+ struct ibv_exp_create_mr_resp *resp, size_t lib_resp_sz, size_t drv_resp_sz);
+int ibv_exp_cmd_query_mkey(struct ibv_context *context,
+ struct ibv_mr *mr,
+ struct ibv_exp_mkey_attr *mkey_attr,
+ struct ibv_exp_query_mkey *cmd, size_t lib_cmd_sz, size_t drv_cmd_sz,
+ struct ibv_exp_query_mkey_resp *resp, size_t lib_resp_sz, size_t drv_resp_sz);
+int ibv_cmd_exp_reg_mr(const struct ibv_exp_reg_mr_in *mr_init_attr,
+ uint64_t hca_va, struct ibv_mr *mr,
+ struct ibv_exp_reg_mr *cmd,
+ size_t cmd_size,
+ struct ibv_exp_reg_mr_resp *resp,
+ size_t resp_size);
+int ibv_cmd_exp_prefetch_mr(struct ibv_mr *mr,
+ struct ibv_exp_prefetch_attr *attr);
+int ibv_exp_cmd_create_wq(struct ibv_context *context,
+ struct ibv_exp_wq_init_attr *wq_init_attr,
+ struct ibv_exp_wq *wq,
+ struct ibv_exp_create_wq *cmd,
+ size_t cmd_core_size,
+ size_t cmd_size,
+ struct ibv_exp_create_wq_resp *resp,
+ size_t resp_core_size,
+ size_t resp_size);
+int ibv_exp_cmd_destroy_wq(struct ibv_exp_wq *wq);
+int ibv_exp_cmd_modify_wq(struct ibv_exp_wq *wq, struct ibv_exp_wq_attr *attr,
+ struct ib_exp_modify_wq *cmd, size_t cmd_size);
+int ibv_exp_cmd_create_rwq_ind_table(struct ibv_context *context,
+ struct ibv_exp_rwq_ind_table_init_attr *init_attr,
+ struct ibv_exp_rwq_ind_table *rwq_ind_table,
+ struct ibv_exp_create_rwq_ind_table *cmd,
+ size_t cmd_core_size,
+ size_t cmd_size,
+ struct ibv_exp_create_rwq_ind_table_resp *resp,
+ size_t resp_core_size,
+ size_t resp_size);
+int ibv_exp_cmd_destroy_rwq_ind_table(struct ibv_exp_rwq_ind_table *rwq_ind_table);
+int ibv_exp_cmd_rereg_mr(struct ibv_mr *mr, uint32_t flags, void *addr,
+ size_t length, uint64_t hca_va, int access,
+ struct ibv_pd *pd, struct ibv_exp_rereg_mr_attr *attr,
+ struct ibv_exp_rereg_mr *cmd,
+ size_t lib_cmd_sz, size_t drv_cmd_sz,
+ struct ibv_exp_rereg_mr_resp *resp,
+ size_t lib_resp_sz, size_t drv_resp_sz);
+/*
+ * ibv_exp_cmd_getenv
+ *
+ * @context: context to the device
+ * @name: the name of the variable to read
+ * @value: pointer where the value of the variable will be written
+ * @n: number of bytes pointed to by val
+ *
+ * return: 0 success
+ * < 0 varaible was not found
+ > 0 variable found but not enuogh space provided. requied space is the value returned.
+ */
+int ibv_exp_cmd_getenv(struct ibv_context *context,
+ const char *name, char *value, size_t n);
+
+
+#endif /* INFINIBAND_DRIVER_EXP_H */
diff --git a/external_libs/ibverbs/include/infiniband/kern-abi.h b/external_libs/ibverbs/include/infiniband/kern-abi.h
new file mode 100644
index 00000000..a6964bb7
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/kern-abi.h
@@ -0,0 +1,1144 @@
+/*
+ * Copyright (c) 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2005, 2006 Cisco Systems. All rights reserved.
+ * Copyright (c) 2005 PathScale, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef KERN_ABI_H
+#define KERN_ABI_H
+
+#include <linux/types.h>
+
+/*
+ * This file must be kept in sync with the kernel's version of
+ * drivers/infiniband/include/ib_user_verbs.h
+ */
+
+/*
+ * The minimum and maximum kernel ABI that we can handle.
+ */
+#define IB_USER_VERBS_MIN_ABI_VERSION 3
+#define IB_USER_VERBS_MAX_ABI_VERSION 6
+
+#define IB_USER_VERBS_CMD_THRESHOLD 50
+
+enum {
+ IB_USER_VERBS_CMD_GET_CONTEXT,
+ IB_USER_VERBS_CMD_QUERY_DEVICE,
+ IB_USER_VERBS_CMD_QUERY_PORT,
+ IB_USER_VERBS_CMD_ALLOC_PD,
+ IB_USER_VERBS_CMD_DEALLOC_PD,
+ IB_USER_VERBS_CMD_CREATE_AH,
+ IB_USER_VERBS_CMD_MODIFY_AH,
+ IB_USER_VERBS_CMD_QUERY_AH,
+ IB_USER_VERBS_CMD_DESTROY_AH,
+ IB_USER_VERBS_CMD_REG_MR,
+ IB_USER_VERBS_CMD_REG_SMR,
+ IB_USER_VERBS_CMD_REREG_MR,
+ IB_USER_VERBS_CMD_QUERY_MR,
+ IB_USER_VERBS_CMD_DEREG_MR,
+ IB_USER_VERBS_CMD_ALLOC_MW,
+ IB_USER_VERBS_CMD_BIND_MW,
+ IB_USER_VERBS_CMD_DEALLOC_MW,
+ IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL,
+ IB_USER_VERBS_CMD_CREATE_CQ,
+ IB_USER_VERBS_CMD_RESIZE_CQ,
+ IB_USER_VERBS_CMD_DESTROY_CQ,
+ IB_USER_VERBS_CMD_POLL_CQ,
+ IB_USER_VERBS_CMD_PEEK_CQ,
+ IB_USER_VERBS_CMD_REQ_NOTIFY_CQ,
+ IB_USER_VERBS_CMD_CREATE_QP,
+ IB_USER_VERBS_CMD_QUERY_QP,
+ IB_USER_VERBS_CMD_MODIFY_QP,
+ IB_USER_VERBS_CMD_DESTROY_QP,
+ IB_USER_VERBS_CMD_POST_SEND,
+ IB_USER_VERBS_CMD_POST_RECV,
+ IB_USER_VERBS_CMD_ATTACH_MCAST,
+ IB_USER_VERBS_CMD_DETACH_MCAST,
+ IB_USER_VERBS_CMD_CREATE_SRQ,
+ IB_USER_VERBS_CMD_MODIFY_SRQ,
+ IB_USER_VERBS_CMD_QUERY_SRQ,
+ IB_USER_VERBS_CMD_DESTROY_SRQ,
+ IB_USER_VERBS_CMD_POST_SRQ_RECV,
+ IB_USER_VERBS_CMD_OPEN_XRCD,
+ IB_USER_VERBS_CMD_CLOSE_XRCD,
+ IB_USER_VERBS_CMD_CREATE_XSRQ,
+ IB_USER_VERBS_CMD_OPEN_QP,
+};
+
+
+#define IB_USER_VERBS_CMD_COMMAND_MASK 0xff
+#define IB_USER_VERBS_CMD_FLAGS_MASK 0xff000000u
+#define IB_USER_VERBS_CMD_FLAGS_SHIFT 24
+
+
+#define IB_USER_VERBS_CMD_FLAG_EXTENDED 0x80ul
+
+
+enum {
+ IB_USER_VERBS_CMD_CREATE_FLOW = (IB_USER_VERBS_CMD_FLAG_EXTENDED <<
+ IB_USER_VERBS_CMD_FLAGS_SHIFT) +
+ IB_USER_VERBS_CMD_THRESHOLD,
+ IB_USER_VERBS_CMD_DESTROY_FLOW
+};
+
+/*
+ * Make sure that all structs defined in this file remain laid out so
+ * that they pack the same way on 32-bit and 64-bit architectures (to
+ * avoid incompatibility between 32-bit userspace and 64-bit kernels).
+ * Specifically:
+ * - Do not use pointer types -- pass pointers in __u64 instead.
+ * - Make sure that any structure larger than 4 bytes is padded to a
+ * multiple of 8 bytes. Otherwise the structure size will be
+ * different between 32-bit and 64-bit architectures.
+ */
+
+struct hdr {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+};
+
+struct response_hdr {
+ __u64 response;
+};
+
+struct ex_hdr {
+ struct {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ };
+ struct {
+ __u64 response;
+ };
+ struct {
+ __u16 provider_in_words;
+ __u16 provider_out_words;
+ __u32 reserved;
+ };
+};
+
+enum ibv_event_rsc_type {
+ IBV_EVENT_RSC_CQ,
+ IBV_EVENT_RSC_QP,
+ IBV_EVENT_RSC_DCT,
+ IBV_EVENT_RSC_SRQ,
+ IBV_EVENT_RSC_DEVICE,
+};
+
+struct ibv_kern_async_event {
+ __u64 element;
+ __u32 event_type;
+ __u32 rsc_type;
+};
+
+struct ibv_comp_event {
+ __u64 cq_handle;
+};
+
+/*
+ * All commands from userspace should start with a __u32 command field
+ * followed by __u16 in_words and out_words fields (which give the
+ * length of the command block and response buffer if any in 32-bit
+ * words). The kernel driver will read these fields first and read
+ * the rest of the command struct based on these value.
+ */
+
+#define IBV_RESP_TO_VERBS_RESP_EX_RAW(ex_ptr, ex_type, ibv_type, field) \
+ ((ibv_type *)((void *)(ex_ptr) + offsetof(ex_type, \
+ field) + sizeof((ex_ptr)->field)))
+
+#define IBV_RESP_TO_VERBS_RESP_EX(ex_ptr, ex_type, ibv_type) \
+ IBV_RESP_TO_VERBS_RESP_EX_RAW(ex_ptr, ex_type, ibv_type, comp_mask)
+
+struct ibv_query_params {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+};
+
+struct ibv_query_params_resp {
+ __u32 num_cq_events;
+};
+
+struct ibv_get_context {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 driver_data[0];
+};
+
+struct ibv_get_context_resp {
+ __u32 async_fd;
+ __u32 num_comp_vectors;
+};
+
+struct ibv_query_device {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 driver_data[0];
+};
+
+struct ibv_query_device_resp {
+ __u64 fw_ver;
+ __u64 node_guid;
+ __u64 sys_image_guid;
+ __u64 max_mr_size;
+ __u64 page_size_cap;
+ __u32 vendor_id;
+ __u32 vendor_part_id;
+ __u32 hw_ver;
+ __u32 max_qp;
+ __u32 max_qp_wr;
+ __u32 device_cap_flags;
+ __u32 max_sge;
+ __u32 max_sge_rd;
+ __u32 max_cq;
+ __u32 max_cqe;
+ __u32 max_mr;
+ __u32 max_pd;
+ __u32 max_qp_rd_atom;
+ __u32 max_ee_rd_atom;
+ __u32 max_res_rd_atom;
+ __u32 max_qp_init_rd_atom;
+ __u32 max_ee_init_rd_atom;
+ __u32 atomic_cap;
+ __u32 max_ee;
+ __u32 max_rdd;
+ __u32 max_mw;
+ __u32 max_raw_ipv6_qp;
+ __u32 max_raw_ethy_qp;
+ __u32 max_mcast_grp;
+ __u32 max_mcast_qp_attach;
+ __u32 max_total_mcast_qp_attach;
+ __u32 max_ah;
+ __u32 max_fmr;
+ __u32 max_map_per_fmr;
+ __u32 max_srq;
+ __u32 max_srq_wr;
+ __u32 max_srq_sge;
+ __u16 max_pkeys;
+ __u8 local_ca_ack_delay;
+ __u8 phys_port_cnt;
+ __u8 reserved[4];
+};
+
+struct ibv_query_port {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u8 port_num;
+ __u8 reserved[7];
+ __u64 driver_data[0];
+};
+
+struct ibv_query_port_resp {
+ __u32 port_cap_flags;
+ __u32 max_msg_sz;
+ __u32 bad_pkey_cntr;
+ __u32 qkey_viol_cntr;
+ __u32 gid_tbl_len;
+ __u16 pkey_tbl_len;
+ __u16 lid;
+ __u16 sm_lid;
+ __u8 state;
+ __u8 max_mtu;
+ __u8 active_mtu;
+ __u8 lmc;
+ __u8 max_vl_num;
+ __u8 sm_sl;
+ __u8 subnet_timeout;
+ __u8 init_type_reply;
+ __u8 active_width;
+ __u8 active_speed;
+ __u8 phys_state;
+ __u8 link_layer;
+ __u8 reserved[2];
+};
+
+struct ibv_alloc_pd {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 driver_data[0];
+};
+
+struct ibv_alloc_pd_resp {
+ __u32 pd_handle;
+};
+
+struct ibv_dealloc_pd {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u32 pd_handle;
+};
+
+struct ibv_open_xrcd {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 fd;
+ __u32 oflags;
+ __u64 driver_data[0];
+};
+
+struct ibv_open_xrcd_resp {
+ __u32 xrcd_handle;
+};
+
+struct ibv_close_xrcd {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u32 xrcd_handle;
+};
+
+struct ibv_reg_mr {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 start;
+ __u64 length;
+ __u64 hca_va;
+ __u32 pd_handle;
+ __u32 access_flags;
+ __u64 driver_data[0];
+};
+
+struct ibv_reg_mr_resp {
+ __u32 mr_handle;
+ __u32 lkey;
+ __u32 rkey;
+};
+
+struct ibv_dereg_mr {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u32 mr_handle;
+};
+
+struct ibv_alloc_mw {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 pd_handle;
+ __u8 mw_type;
+ __u8 reserved[3];
+};
+
+struct ibv_alloc_mw_resp {
+ __u32 mw_handle;
+ __u32 rkey;
+};
+
+struct ibv_dealloc_mw {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u32 mw_handle;
+};
+
+struct ibv_create_comp_channel {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+};
+
+struct ibv_create_comp_channel_resp {
+ __u32 fd;
+};
+
+struct ibv_create_cq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 user_handle;
+ __u32 cqe;
+ __u32 comp_vector;
+ __s32 comp_channel;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_create_cq_resp {
+ __u32 cq_handle;
+ __u32 cqe;
+};
+
+struct ibv_kern_wc {
+ __u64 wr_id;
+ __u32 status;
+ __u32 opcode;
+ __u32 vendor_err;
+ __u32 byte_len;
+ __u32 imm_data;
+ __u32 qp_num;
+ __u32 src_qp;
+ __u32 wc_flags;
+ __u16 pkey_index;
+ __u16 slid;
+ __u8 sl;
+ __u8 dlid_path_bits;
+ __u8 port_num;
+ __u8 reserved;
+};
+
+struct ibv_poll_cq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 cq_handle;
+ __u32 ne;
+};
+
+struct ibv_poll_cq_resp {
+ __u32 count;
+ __u32 reserved;
+ struct ibv_kern_wc wc[0];
+};
+
+struct ibv_req_notify_cq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u32 cq_handle;
+ __u32 solicited;
+};
+
+struct ibv_resize_cq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 cq_handle;
+ __u32 cqe;
+ __u64 driver_data[0];
+};
+
+struct ibv_resize_cq_resp {
+ __u32 cqe;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_destroy_cq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 cq_handle;
+ __u32 reserved;
+};
+
+struct ibv_destroy_cq_resp {
+ __u32 comp_events_reported;
+ __u32 async_events_reported;
+};
+
+struct ibv_kern_global_route {
+ __u8 dgid[16];
+ __u32 flow_label;
+ __u8 sgid_index;
+ __u8 hop_limit;
+ __u8 traffic_class;
+ __u8 reserved;
+};
+
+struct ibv_kern_ah_attr {
+ struct ibv_kern_global_route grh;
+ __u16 dlid;
+ __u8 sl;
+ __u8 src_path_bits;
+ __u8 static_rate;
+ __u8 is_global;
+ __u8 port_num;
+ __u8 reserved;
+};
+
+struct ibv_kern_qp_attr {
+ __u32 qp_attr_mask;
+ __u32 qp_state;
+ __u32 cur_qp_state;
+ __u32 path_mtu;
+ __u32 path_mig_state;
+ __u32 qkey;
+ __u32 rq_psn;
+ __u32 sq_psn;
+ __u32 dest_qp_num;
+ __u32 qp_access_flags;
+
+ struct ibv_kern_ah_attr ah_attr;
+ struct ibv_kern_ah_attr alt_ah_attr;
+
+ /* ib_qp_cap */
+ __u32 max_send_wr;
+ __u32 max_recv_wr;
+ __u32 max_send_sge;
+ __u32 max_recv_sge;
+ __u32 max_inline_data;
+
+ __u16 pkey_index;
+ __u16 alt_pkey_index;
+ __u8 en_sqd_async_notify;
+ __u8 sq_draining;
+ __u8 max_rd_atomic;
+ __u8 max_dest_rd_atomic;
+ __u8 min_rnr_timer;
+ __u8 port_num;
+ __u8 timeout;
+ __u8 retry_cnt;
+ __u8 rnr_retry;
+ __u8 alt_port_num;
+ __u8 alt_timeout;
+ __u8 reserved[5];
+};
+
+struct ibv_create_qp {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 user_handle;
+ __u32 pd_handle;
+ __u32 send_cq_handle;
+ __u32 recv_cq_handle;
+ __u32 srq_handle;
+ __u32 max_send_wr;
+ __u32 max_recv_wr;
+ __u32 max_send_sge;
+ __u32 max_recv_sge;
+ __u32 max_inline_data;
+ __u8 sq_sig_all;
+ __u8 qp_type;
+ __u8 is_srq;
+ __u8 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_open_qp {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 user_handle;
+ __u32 pd_handle;
+ __u32 qpn;
+ __u8 qp_type;
+ __u8 reserved[7];
+ __u64 driver_data[0];
+};
+
+/* also used for open response */
+struct ibv_create_qp_resp {
+ __u32 qp_handle;
+ __u32 qpn;
+ __u32 max_send_wr;
+ __u32 max_recv_wr;
+ __u32 max_send_sge;
+ __u32 max_recv_sge;
+ __u32 max_inline_data;
+ __u32 reserved;
+};
+
+enum ibv_create_qp_ex_comp_mask {
+ IBV_CREATE_QP_EX_CAP_FLAGS = (1ULL << 0)
+};
+
+struct ibv_create_qp_ex {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u16 provider_in_words;
+ __u16 provider_out_words;
+ __u32 cmd_hdr_reserved;
+ __u64 comp_mask;
+ __u64 response;
+ __u64 user_handle;
+ __u32 pd_handle;
+ __u32 send_cq_handle;
+ __u32 recv_cq_handle;
+ __u32 srq_handle;
+ __u32 max_send_wr;
+ __u32 max_recv_wr;
+ __u32 max_send_sge;
+ __u32 max_recv_sge;
+ __u32 max_inline_data;
+ __u8 sq_sig_all;
+ __u8 qp_type;
+ __u8 is_srq;
+ __u8 reserved;
+ __u64 qp_cap_flags;
+ __u64 driver_data[0];
+};
+
+struct ibv_create_qp_resp_ex {
+ __u64 comp_mask;
+ __u32 qp_handle;
+ __u32 qpn;
+ __u32 max_send_wr;
+ __u32 max_recv_wr;
+ __u32 max_send_sge;
+ __u32 max_recv_sge;
+ __u32 max_inline_data;
+ __u32 reserved;
+};
+
+struct ibv_qp_dest {
+ __u8 dgid[16];
+ __u32 flow_label;
+ __u16 dlid;
+ __u16 reserved;
+ __u8 sgid_index;
+ __u8 hop_limit;
+ __u8 traffic_class;
+ __u8 sl;
+ __u8 src_path_bits;
+ __u8 static_rate;
+ __u8 is_global;
+ __u8 port_num;
+};
+
+struct ibv_query_qp {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 qp_handle;
+ __u32 attr_mask;
+ __u64 driver_data[0];
+};
+
+struct ibv_query_qp_resp {
+ struct ibv_qp_dest dest;
+ struct ibv_qp_dest alt_dest;
+ __u32 max_send_wr;
+ __u32 max_recv_wr;
+ __u32 max_send_sge;
+ __u32 max_recv_sge;
+ __u32 max_inline_data;
+ __u32 qkey;
+ __u32 rq_psn;
+ __u32 sq_psn;
+ __u32 dest_qp_num;
+ __u32 qp_access_flags;
+ __u16 pkey_index;
+ __u16 alt_pkey_index;
+ __u8 qp_state;
+ __u8 cur_qp_state;
+ __u8 path_mtu;
+ __u8 path_mig_state;
+ __u8 sq_draining;
+ __u8 max_rd_atomic;
+ __u8 max_dest_rd_atomic;
+ __u8 min_rnr_timer;
+ __u8 port_num;
+ __u8 timeout;
+ __u8 retry_cnt;
+ __u8 rnr_retry;
+ __u8 alt_port_num;
+ __u8 alt_timeout;
+ __u8 sq_sig_all;
+ __u8 reserved[5];
+ __u64 driver_data[0];
+};
+
+struct ibv_modify_qp {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ struct ibv_qp_dest dest;
+ struct ibv_qp_dest alt_dest;
+ __u32 qp_handle;
+ __u32 attr_mask;
+ __u32 qkey;
+ __u32 rq_psn;
+ __u32 sq_psn;
+ __u32 dest_qp_num;
+ __u32 qp_access_flags;
+ __u16 pkey_index;
+ __u16 alt_pkey_index;
+ __u8 qp_state;
+ __u8 cur_qp_state;
+ __u8 path_mtu;
+ __u8 path_mig_state;
+ __u8 en_sqd_async_notify;
+ __u8 max_rd_atomic;
+ __u8 max_dest_rd_atomic;
+ __u8 min_rnr_timer;
+ __u8 port_num;
+ __u8 timeout;
+ __u8 retry_cnt;
+ __u8 rnr_retry;
+ __u8 alt_port_num;
+ __u8 alt_timeout;
+ __u8 reserved[2];
+ __u64 driver_data[0];
+};
+
+struct ibv_destroy_qp {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 qp_handle;
+ __u32 reserved;
+};
+
+struct ibv_destroy_qp_resp {
+ __u32 events_reported;
+};
+
+struct ibv_kern_send_wr {
+ __u64 wr_id;
+ __u32 num_sge;
+ __u32 opcode;
+ __u32 send_flags;
+ __u32 imm_data;
+ union {
+ struct {
+ __u64 remote_addr;
+ __u32 rkey;
+ __u32 reserved;
+ } rdma;
+ struct {
+ __u64 remote_addr;
+ __u64 compare_add;
+ __u64 swap;
+ __u32 rkey;
+ __u32 reserved;
+ } atomic;
+ struct {
+ __u32 ah;
+ __u32 remote_qpn;
+ __u32 remote_qkey;
+ __u32 reserved;
+ } ud;
+ } wr;
+ union {
+ struct {
+ __u32 remote_srqn;
+ } xrc;
+ } qp_type;
+};
+
+struct ibv_kern_eth_filter {
+ __u8 dst_mac[6];
+ __u8 src_mac[6];
+ __u16 ether_type;
+ __u16 vlan_tag;
+};
+
+struct ibv_kern_spec_eth {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ struct ibv_kern_eth_filter val;
+ struct ibv_kern_eth_filter mask;
+};
+
+struct ibv_kern_ib_filter {
+ __u32 qpn;
+ __u8 dst_gid[16];
+};
+
+struct ibv_kern_spec_ib {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ struct ibv_kern_ib_filter val;
+ struct ibv_kern_ib_filter mask;
+};
+
+struct ibv_kern_ipv4_filter {
+ __u32 src_ip;
+ __u32 dst_ip;
+};
+
+struct ibv_kern_spec_ipv4 {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ struct ibv_kern_ipv4_filter val;
+ struct ibv_kern_ipv4_filter mask;
+};
+
+struct ibv_kern_tcp_udp_filter {
+ __u16 dst_port;
+ __u16 src_port;
+};
+
+struct ibv_kern_spec_tcp_udp {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ struct ibv_kern_tcp_udp_filter val;
+ struct ibv_kern_tcp_udp_filter mask;
+};
+
+
+struct ibv_kern_spec {
+ union {
+ struct {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ } hdr;
+ struct ibv_kern_spec_ib ib;
+ struct ibv_kern_spec_eth eth;
+ struct ibv_kern_spec_ipv4 ipv4;
+ struct ibv_kern_spec_tcp_udp tcp_udp;
+ };
+
+};
+
+struct ibv_kern_flow_attr {
+ __u32 type;
+ __u16 size;
+ __u16 priority;
+ __u8 num_of_specs;
+ __u8 reserved[2];
+ __u8 port;
+ __u32 flags;
+ /* Following are the optional layers according to user request
+ * struct ibv_kern_flow_spec_xxx
+ * struct ibv_kern_flow_spec_yyy
+ */
+};
+
+struct ibv_post_send {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 qp_handle;
+ __u32 wr_count;
+ __u32 sge_count;
+ __u32 wqe_size;
+ struct ibv_kern_send_wr send_wr[0];
+};
+
+struct ibv_post_send_resp {
+ __u32 bad_wr;
+};
+
+struct ibv_kern_recv_wr {
+ __u64 wr_id;
+ __u32 num_sge;
+ __u32 reserved;
+};
+
+struct ibv_post_recv {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 qp_handle;
+ __u32 wr_count;
+ __u32 sge_count;
+ __u32 wqe_size;
+ struct ibv_kern_recv_wr recv_wr[0];
+};
+
+struct ibv_post_recv_resp {
+ __u32 bad_wr;
+};
+
+struct ibv_post_srq_recv {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 srq_handle;
+ __u32 wr_count;
+ __u32 sge_count;
+ __u32 wqe_size;
+ struct ibv_kern_recv_wr recv_wr[0];
+};
+
+struct ibv_post_srq_recv_resp {
+ __u32 bad_wr;
+};
+
+struct ibv_create_ah {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 user_handle;
+ __u32 pd_handle;
+ __u32 reserved;
+ struct ibv_kern_ah_attr attr;
+};
+
+struct ibv_create_ah_resp {
+ __u32 handle;
+};
+
+struct ibv_destroy_ah {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u32 ah_handle;
+};
+
+struct ibv_attach_mcast {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u8 gid[16];
+ __u32 qp_handle;
+ __u16 mlid;
+ __u16 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_detach_mcast {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u8 gid[16];
+ __u32 qp_handle;
+ __u16 mlid;
+ __u16 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_create_flow {
+ struct ex_hdr hdr;
+ __u32 comp_mask;
+ __u32 qp_handle;
+ struct ibv_kern_flow_attr flow_attr;
+};
+
+struct ibv_create_flow_resp {
+ __u32 comp_mask;
+ __u32 flow_handle;
+};
+
+struct ibv_destroy_flow {
+ struct ex_hdr hdr;
+ __u32 comp_mask;
+ __u32 flow_handle;
+};
+
+struct ibv_create_srq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 user_handle;
+ __u32 pd_handle;
+ __u32 max_wr;
+ __u32 max_sge;
+ __u32 srq_limit;
+ __u64 driver_data[0];
+};
+
+struct ibv_create_xsrq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u64 user_handle;
+ __u32 srq_type;
+ __u32 pd_handle;
+ __u32 max_wr;
+ __u32 max_sge;
+ __u32 srq_limit;
+ __u32 reserved;
+ __u32 xrcd_handle;
+ __u32 cq_handle;
+ __u64 driver_data[0];
+};
+
+struct ibv_create_srq_resp {
+ __u32 srq_handle;
+ __u32 max_wr;
+ __u32 max_sge;
+ __u32 srqn;
+};
+
+struct ibv_modify_srq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u32 srq_handle;
+ __u32 attr_mask;
+ __u32 max_wr;
+ __u32 srq_limit;
+ __u64 driver_data[0];
+};
+
+struct ibv_query_srq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 srq_handle;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_query_srq_resp {
+ __u32 max_wr;
+ __u32 max_sge;
+ __u32 srq_limit;
+ __u32 reserved;
+};
+
+struct ibv_destroy_srq {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u64 response;
+ __u32 srq_handle;
+ __u32 reserved;
+};
+
+struct ibv_destroy_srq_resp {
+ __u32 events_reported;
+};
+
+/*
+ * Compatibility with older ABI versions
+ */
+
+enum {
+ IB_USER_VERBS_CMD_QUERY_PARAMS_V2,
+ IB_USER_VERBS_CMD_GET_CONTEXT_V2,
+ IB_USER_VERBS_CMD_QUERY_DEVICE_V2,
+ IB_USER_VERBS_CMD_QUERY_PORT_V2,
+ IB_USER_VERBS_CMD_QUERY_GID_V2,
+ IB_USER_VERBS_CMD_QUERY_PKEY_V2,
+ IB_USER_VERBS_CMD_ALLOC_PD_V2,
+ IB_USER_VERBS_CMD_DEALLOC_PD_V2,
+ IB_USER_VERBS_CMD_CREATE_AH_V2,
+ IB_USER_VERBS_CMD_MODIFY_AH_V2,
+ IB_USER_VERBS_CMD_QUERY_AH_V2,
+ IB_USER_VERBS_CMD_DESTROY_AH_V2,
+ IB_USER_VERBS_CMD_REG_MR_V2,
+ IB_USER_VERBS_CMD_REG_SMR_V2,
+ IB_USER_VERBS_CMD_REREG_MR_V2,
+ IB_USER_VERBS_CMD_QUERY_MR_V2,
+ IB_USER_VERBS_CMD_DEREG_MR_V2,
+ IB_USER_VERBS_CMD_ALLOC_MW_V2,
+ IB_USER_VERBS_CMD_BIND_MW_V2,
+ IB_USER_VERBS_CMD_DEALLOC_MW_V2,
+ IB_USER_VERBS_CMD_CREATE_CQ_V2,
+ IB_USER_VERBS_CMD_RESIZE_CQ_V2,
+ IB_USER_VERBS_CMD_DESTROY_CQ_V2,
+ IB_USER_VERBS_CMD_POLL_CQ_V2,
+ IB_USER_VERBS_CMD_PEEK_CQ_V2,
+ IB_USER_VERBS_CMD_REQ_NOTIFY_CQ_V2,
+ IB_USER_VERBS_CMD_CREATE_QP_V2,
+ IB_USER_VERBS_CMD_QUERY_QP_V2,
+ IB_USER_VERBS_CMD_MODIFY_QP_V2,
+ IB_USER_VERBS_CMD_DESTROY_QP_V2,
+ IB_USER_VERBS_CMD_POST_SEND_V2,
+ IB_USER_VERBS_CMD_POST_RECV_V2,
+ IB_USER_VERBS_CMD_ATTACH_MCAST_V2,
+ IB_USER_VERBS_CMD_DETACH_MCAST_V2,
+ IB_USER_VERBS_CMD_CREATE_SRQ_V2,
+ IB_USER_VERBS_CMD_MODIFY_SRQ_V2,
+ IB_USER_VERBS_CMD_QUERY_SRQ_V2,
+ IB_USER_VERBS_CMD_DESTROY_SRQ_V2,
+ IB_USER_VERBS_CMD_POST_SRQ_RECV_V2,
+ /*
+ * Set commands that didn't exist to -1 so our compile-time
+ * trick opcodes in IBV_INIT_CMD() doesn't break.
+ */
+ IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL_V2 = -1,
+ IB_USER_VERBS_CMD_CREATE_QP_EX_V2 = -1,
+ IB_USER_VERBS_CMD_MODIFY_CQ_EX_V2 = -1,
+ IB_USER_VERBS_CMD_CREATE_FLOW_V2 = -1,
+ IB_USER_VERBS_CMD_DESTROY_FLOW_V2 = -1,
+ IB_USER_VERBS_CMD_OPEN_XRCD_V2 = -1,
+ IB_USER_VERBS_CMD_CLOSE_XRCD_V2 = -1,
+ IB_USER_VERBS_CMD_CREATE_XSRQ_V2 = -1,
+ IB_USER_VERBS_CMD_OPEN_QP_V2 = -1,
+ IB_USER_VERBS_CMD_MODIFY_QP_EX_V2 = -1,
+ IB_USER_VERBS_CMD_CREATE_CQ_EX_V2 = -1,
+ IB_USER_VERBS_CMD_QUERY_DEVICE_EX_V2 = -1,
+ IB_USER_VERBS_CMD_CREATE_DCT_V2 = -1,
+ IB_USER_VERBS_CMD_DESTROY_DCT_V2 = -1,
+ IB_USER_VERBS_CMD_QUERY_DCT_V2 = -1,
+ IB_USER_VERBS_CMD_EXP_REG_MR_V2 = -1,
+ IB_USER_VERBS_CMD_EXP_PREFETCH_MR_V2 = -1,
+};
+
+struct ibv_modify_srq_v3 {
+ __u32 command;
+ __u16 in_words;
+ __u16 out_words;
+ __u32 srq_handle;
+ __u32 attr_mask;
+ __u32 max_wr;
+ __u32 max_sge;
+ __u32 srq_limit;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_create_qp_resp_v3 {
+ __u32 qp_handle;
+ __u32 qpn;
+};
+
+struct ibv_create_qp_resp_v4 {
+ __u32 qp_handle;
+ __u32 qpn;
+ __u32 max_send_wr;
+ __u32 max_recv_wr;
+ __u32 max_send_sge;
+ __u32 max_recv_sge;
+ __u32 max_inline_data;
+};
+
+struct ibv_create_srq_resp_v5 {
+ __u32 srq_handle;
+};
+
+#endif /* KERN_ABI_H */
diff --git a/external_libs/ibverbs/include/infiniband/kern-abi_exp.h b/external_libs/ibverbs/include/infiniband/kern-abi_exp.h
new file mode 100644
index 00000000..b03f19fb
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/kern-abi_exp.h
@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2005, 2006 Cisco Systems. All rights reserved.
+ * Copyright (c) 2005 PathScale, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef KERN_ABI_EXP_H
+#define KERN_ABI_EXP_H
+
+#include <infiniband/kern-abi.h>
+
+/*
+ * This file must be kept in sync with the kernel's version of
+ * drivers/infiniband/include/ib_user_verbs_exp.h
+ */
+
+enum {
+ IB_USER_VERBS_EXP_CMD_FIRST = 64
+};
+
+enum {
+ IB_USER_VERBS_EXP_CMD_CREATE_QP,
+ IB_USER_VERBS_EXP_CMD_MODIFY_CQ,
+ IB_USER_VERBS_EXP_CMD_MODIFY_QP,
+ IB_USER_VERBS_EXP_CMD_CREATE_CQ,
+ IB_USER_VERBS_EXP_CMD_QUERY_DEVICE,
+ IB_USER_VERBS_EXP_CMD_CREATE_DCT,
+ IB_USER_VERBS_EXP_CMD_DESTROY_DCT,
+ IB_USER_VERBS_EXP_CMD_QUERY_DCT,
+ IB_USER_VERBS_EXP_CMD_ARM_DCT,
+ IB_USER_VERBS_EXP_CMD_CREATE_MR,
+ IB_USER_VERBS_EXP_CMD_QUERY_MKEY,
+ IB_USER_VERBS_EXP_CMD_REG_MR,
+ IB_USER_VERBS_EXP_CMD_PREFETCH_MR,
+ IB_USER_VERBS_EXP_CMD_REREG_MR,
+ IB_USER_VERBS_EXP_CMD_CREATE_WQ,
+ IB_USER_VERBS_EXP_CMD_MODIFY_WQ,
+ IB_USER_VERBS_EXP_CMD_DESTROY_WQ,
+ IB_USER_VERBS_EXP_CMD_CREATE_RWQ_IND_TBL,
+ IB_USER_VERBS_EXP_CMD_DESTROY_RWQ_IND_TBL,
+ IB_USER_VERBS_EXP_CMD_CREATE_FLOW,
+};
+
+enum {
+ IB_USER_VERBS_CMD_EXP_CREATE_WQ =
+ IB_USER_VERBS_EXP_CMD_CREATE_WQ +
+ IB_USER_VERBS_EXP_CMD_FIRST,
+ IB_USER_VERBS_CMD_EXP_MODIFY_WQ =
+ IB_USER_VERBS_EXP_CMD_MODIFY_WQ +
+ IB_USER_VERBS_EXP_CMD_FIRST,
+ IB_USER_VERBS_CMD_EXP_DESTROY_WQ =
+ IB_USER_VERBS_EXP_CMD_DESTROY_WQ +
+ IB_USER_VERBS_EXP_CMD_FIRST,
+ IB_USER_VERBS_CMD_EXP_CREATE_RWQ_IND_TBL =
+ IB_USER_VERBS_EXP_CMD_CREATE_RWQ_IND_TBL +
+ IB_USER_VERBS_EXP_CMD_FIRST,
+ IB_USER_VERBS_CMD_EXP_DESTROY_RWQ_IND_TBL =
+ IB_USER_VERBS_EXP_CMD_DESTROY_RWQ_IND_TBL +
+ IB_USER_VERBS_EXP_CMD_FIRST,
+ /*
+ * Set commands that didn't exist to -1 so our compile-time
+ * trick opcodes in IBV_INIT_CMD() doesn't break.
+ */
+ IB_USER_VERBS_CMD_EXP_CREATE_WQ_V2 = -1,
+ IB_USER_VERBS_CMD_EXP_MODIFY_WQ_V2 = -1,
+ IB_USER_VERBS_CMD_EXP_DESTROY_WQ_V2 = -1,
+ IB_USER_VERBS_CMD_EXP_CREATE_RWQ_IND_TBL_V2 = -1,
+ IB_USER_VERBS_CMD_EXP_DESTROY_RWQ_IND_TBL_V2 = -1,
+};
+
+enum ibv_exp_create_qp_comp_mask {
+ IBV_EXP_CREATE_QP_CAP_FLAGS = (1ULL << 0),
+ IBV_EXP_CREATE_QP_INL_RECV = (1ULL << 1),
+ IBV_EXP_CREATE_QP_QPG = (1ULL << 2),
+ IBV_EXP_CREATE_QP_MAX_INL_KLMS = (1ULL << 3)
+};
+
+struct ibv_create_qpg_init_attrib {
+ __u32 tss_child_count;
+ __u32 rss_child_count;
+};
+
+struct ibv_create_qpg {
+ __u32 qpg_type;
+ union {
+ struct {
+ __u32 parent_handle;
+ __u32 reserved;
+ };
+ struct ibv_create_qpg_init_attrib parent_attrib;
+ };
+ __u32 reserved2;
+};
+
+enum ibv_exp_create_qp_kernel_flags {
+ IBV_EXP_CREATE_QP_KERNEL_FLAGS = IBV_EXP_QP_CREATE_CROSS_CHANNEL |
+ IBV_EXP_QP_CREATE_MANAGED_SEND |
+ IBV_EXP_QP_CREATE_MANAGED_RECV |
+ IBV_EXP_QP_CREATE_ATOMIC_BE_REPLY |
+ IBV_EXP_QP_CREATE_RX_END_PADDING |
+ IBV_EXP_QP_CREATE_SCATTER_FCS
+};
+
+struct ibv_exp_create_qp {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u64 user_handle;
+ __u32 pd_handle;
+ __u32 send_cq_handle;
+ __u32 recv_cq_handle;
+ __u32 srq_handle;
+ __u32 max_send_wr;
+ __u32 max_recv_wr;
+ __u32 max_send_sge;
+ __u32 max_recv_sge;
+ __u32 max_inline_data;
+ __u8 sq_sig_all;
+ __u8 qp_type;
+ __u8 is_srq;
+ __u8 reserved;
+ __u64 qp_cap_flags;
+ __u32 max_inl_recv;
+ __u32 reserved1;
+ struct ibv_create_qpg qpg;
+ __u64 max_inl_send_klms;
+ struct {
+ __u64 rx_hash_fields_mask;
+ __u32 rwq_ind_tbl_handle;
+ __u8 rx_hash_function;
+ __u8 rx_hash_key_len;
+ __u8 rx_hash_key[128];
+ __u16 reserved;
+ } rx_hash_info;
+ __u8 port_num;
+ __u8 reserved_2[7];
+ __u64 driver_data[0];
+};
+
+enum ibv_exp_create_qp_resp_comp_mask {
+ IBV_EXP_CREATE_QP_RESP_INL_RECV = (1ULL << 0),
+};
+
+struct ibv_exp_create_qp_resp {
+ __u64 comp_mask;
+ __u32 qp_handle;
+ __u32 qpn;
+ __u32 max_send_wr;
+ __u32 max_recv_wr;
+ __u32 max_send_sge;
+ __u32 max_recv_sge;
+ __u32 max_inline_data;
+ __u32 max_inl_recv;
+};
+
+struct ibv_exp_umr_caps_resp {
+ __u32 max_klm_list_size;
+ __u32 max_send_wqe_inline_klms;
+ __u32 max_umr_recursion_depth;
+ __u32 max_umr_stride_dimension;
+};
+
+struct ibv_exp_odp_caps_resp {
+ __u64 general_odp_caps;
+ struct {
+ __u32 rc_odp_caps;
+ __u32 uc_odp_caps;
+ __u32 ud_odp_caps;
+ __u32 dc_odp_caps;
+ __u32 xrc_odp_caps;
+ __u32 raw_eth_odp_caps;
+ } per_transport_caps;
+};
+
+struct ibv_exp_query_device {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_rx_hash_caps_resp {
+ __u32 max_rwq_indirection_tables;
+ __u32 max_rwq_indirection_table_size;
+ __u64 supported_packet_fields;
+ __u32 supported_qps;
+ __u8 supported_hash_functions;
+ __u8 reserved[3];
+};
+
+struct ibv_exp_mp_rq_caps_resp {
+ __u32 supported_qps; /* use ibv_exp_supported_qp_types */
+ __u32 allowed_shifts; /* use ibv_exp_mp_rq_shifts */
+ __u8 min_single_wqe_log_num_of_strides;
+ __u8 max_single_wqe_log_num_of_strides;
+ __u8 min_single_stride_log_num_of_bytes;
+ __u8 max_single_stride_log_num_of_bytes;
+ __u32 reserved;
+};
+
+struct ibv_exp_ec_caps_resp {
+ __u32 max_ec_data_vector_count;
+ __u32 max_ec_calc_inflight_calcs;
+};
+
+struct ibv_exp_masked_atomic_caps {
+ __u32 max_fa_bit_boundary;
+ __u32 log_max_atomic_inline;
+ __u64 masked_log_atomic_arg_sizes;
+ __u64 masked_log_atomic_arg_sizes_network_endianness;
+};
+
+struct ibv_exp_lso_caps_resp {
+ __u32 max_tso;
+ __u32 supported_qpts;
+};
+
+struct ibv_exp_packet_pacing_caps_resp {
+ __u32 qp_rate_limit_min;
+ __u32 qp_rate_limit_max; /* In kbps */
+ __u32 supported_qpts;
+ __u32 reserved;
+};
+
+struct ibv_exp_query_device_resp {
+ __u64 comp_mask;
+ __u64 fw_ver;
+ __u64 node_guid;
+ __u64 sys_image_guid;
+ __u64 max_mr_size;
+ __u64 page_size_cap;
+ __u32 vendor_id;
+ __u32 vendor_part_id;
+ __u32 hw_ver;
+ __u32 max_qp;
+ __u32 max_qp_wr;
+ __u32 device_cap_flags;
+ __u32 max_sge;
+ __u32 max_sge_rd;
+ __u32 max_cq;
+ __u32 max_cqe;
+ __u32 max_mr;
+ __u32 max_pd;
+ __u32 max_qp_rd_atom;
+ __u32 max_ee_rd_atom;
+ __u32 max_res_rd_atom;
+ __u32 max_qp_init_rd_atom;
+ __u32 max_ee_init_rd_atom;
+ __u32 exp_atomic_cap;
+ __u32 max_ee;
+ __u32 max_rdd;
+ __u32 max_mw;
+ __u32 max_raw_ipv6_qp;
+ __u32 max_raw_ethy_qp;
+ __u32 max_mcast_grp;
+ __u32 max_mcast_qp_attach;
+ __u32 max_total_mcast_qp_attach;
+ __u32 max_ah;
+ __u32 max_fmr;
+ __u32 max_map_per_fmr;
+ __u32 max_srq;
+ __u32 max_srq_wr;
+ __u32 max_srq_sge;
+ __u16 max_pkeys;
+ __u8 local_ca_ack_delay;
+ __u8 phys_port_cnt;
+ __u8 reserved[4];
+ __u64 timestamp_mask;
+ __u64 hca_core_clock;
+ __u64 device_cap_flags2;
+ __u32 dc_rd_req;
+ __u32 dc_rd_res;
+ __u32 inline_recv_sz;
+ __u32 max_rss_tbl_sz;
+ __u64 log_atomic_arg_sizes;
+ __u32 max_fa_bit_boundary;
+ __u32 log_max_atomic_inline;
+ struct ibv_exp_umr_caps_resp umr_caps;
+ struct ibv_exp_odp_caps_resp odp_caps;
+ __u32 max_dct;
+ __u32 max_ctx_res_domain;
+ struct ibv_exp_rx_hash_caps_resp rx_hash;
+ __u32 max_wq_type_rq;
+ __u32 max_device_ctx;
+ struct ibv_exp_mp_rq_caps_resp mp_rq_caps;
+ __u16 wq_vlan_offloads_cap;
+ __u8 reserved1[6];
+ struct ibv_exp_ec_caps_resp ec_caps;
+ struct ibv_exp_masked_atomic_caps masked_atomic_caps;
+ __u16 rx_pad_end_addr_align;
+ __u8 reserved2[6];
+ struct ibv_exp_lso_caps_resp tso_caps;
+ struct ibv_exp_packet_pacing_caps_resp packet_pacing_caps;
+};
+
+struct ibv_exp_create_dct {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u64 user_handle;
+ __u32 pd_handle;
+ __u32 cq_handle;
+ __u32 srq_handle;
+ __u32 access_flags;
+ __u64 dc_key;
+ __u32 flow_label;
+ __u8 min_rnr_timer;
+ __u8 tclass;
+ __u8 port;
+ __u8 pkey_index;
+ __u8 gid_index;
+ __u8 hop_limit;
+ __u8 mtu;
+ __u8 rsvd0;
+ __u32 create_flags;
+ __u32 inline_size;
+ __u32 rsvd1;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_create_dct_resp {
+ __u32 dct_handle;
+ __u32 dct_num;
+ __u32 inline_size;
+ __u32 rsvd;
+};
+
+struct ibv_exp_destroy_dct {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u32 dct_handle;
+ __u32 rsvd;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_destroy_dct_resp {
+ __u32 events_reported;
+ __u32 reserved;
+};
+
+struct ibv_exp_query_dct {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u32 dct_handle;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_query_dct_resp {
+ __u64 dc_key;
+ __u32 access_flags;
+ __u32 flow_label;
+ __u32 key_violations;
+ __u8 port;
+ __u8 min_rnr_timer;
+ __u8 tclass;
+ __u8 mtu;
+ __u8 pkey_index;
+ __u8 gid_index;
+ __u8 hop_limit;
+ __u8 state;
+ __u32 rsvd;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_arm_dct {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u32 dct_handle;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_arm_dct_resp {
+ __u64 reserved;
+};
+
+struct ibv_exp_modify_cq {
+ struct ex_hdr hdr;
+ __u32 cq_handle;
+ __u32 attr_mask;
+ __u16 cq_count;
+ __u16 cq_period;
+ __u32 cq_cap_flags;
+ __u32 comp_mask;
+ __u32 rsvd;
+};
+
+struct ibv_exp_modify_qp {
+ struct ex_hdr hdr;
+ __u32 comp_mask;
+ struct ibv_qp_dest dest;
+ struct ibv_qp_dest alt_dest;
+ __u32 qp_handle;
+ __u32 attr_mask;
+ __u32 qkey;
+ __u32 rq_psn;
+ __u32 sq_psn;
+ __u32 dest_qp_num;
+ __u32 qp_access_flags;
+ __u16 pkey_index;
+ __u16 alt_pkey_index;
+ __u8 qp_state;
+ __u8 cur_qp_state;
+ __u8 path_mtu;
+ __u8 path_mig_state;
+ __u8 en_sqd_async_notify;
+ __u8 max_rd_atomic;
+ __u8 max_dest_rd_atomic;
+ __u8 min_rnr_timer;
+ __u8 port_num;
+ __u8 timeout;
+ __u8 retry_cnt;
+ __u8 rnr_retry;
+ __u8 alt_port_num;
+ __u8 alt_timeout;
+ __u8 reserved[6];
+ __u64 dct_key;
+ __u32 exp_attr_mask;
+ __u32 flow_entropy;
+ __u64 driver_data[0];
+ __u32 rate_limit;
+ __u32 reserved1;
+};
+
+enum ibv_exp_create_cq_comp_mask {
+ IBV_EXP_CREATE_CQ_CAP_FLAGS = (uint64_t)1 << 0,
+};
+
+struct ibv_exp_create_cq {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u64 user_handle;
+ __u32 cqe;
+ __u32 comp_vector;
+ __s32 comp_channel;
+ __u32 reserved;
+ __u64 create_flags;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_create_mr {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u32 pd_handle;
+ __u32 max_klm_list_size;
+ __u64 exp_access_flags;
+ __u32 create_flags;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_create_mr_resp {
+ __u64 comp_mask;
+ __u32 handle;
+ __u32 lkey;
+ __u32 rkey;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_query_mkey {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u32 handle;
+ __u32 lkey;
+ __u32 rkey;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+struct ibv_exp_query_mkey_resp {
+ __u64 comp_mask;
+ __u32 max_klm_list_size;
+ __u32 reserved;
+ __u64 driver_data[0];
+};
+
+enum ibv_exp_reg_mr_comp_mask {
+ IBV_EXP_REG_MR_EXP_ACCESS_FLAGS = 1ULL << 0,
+};
+
+struct ibv_exp_reg_mr {
+ struct ex_hdr hdr;
+ __u64 start;
+ __u64 length;
+ __u64 hca_va;
+ __u32 pd_handle;
+ __u32 reserved;
+ __u64 exp_access_flags;
+ __u64 comp_mask;
+};
+
+struct ibv_exp_prefetch_mr {
+ struct ex_hdr hdr;
+ __u64 comp_mask;
+ __u32 mr_handle;
+ __u32 flags;
+ __u64 start;
+ __u64 length;
+};
+
+struct ibv_exp_reg_mr_resp {
+ __u32 mr_handle;
+ __u32 lkey;
+ __u32 rkey;
+ __u32 reserved;
+ __u64 comp_mask;
+};
+
+struct ibv_exp_rereg_mr {
+ struct ex_hdr hdr;
+ __u32 comp_mask;
+ __u32 mr_handle;
+ __u32 flags;
+ __u32 reserved;
+ __u64 start;
+ __u64 length;
+ __u64 hca_va;
+ __u32 pd_handle;
+ __u32 access_flags;
+};
+
+struct ibv_exp_rereg_mr_resp {
+ __u32 comp_mask;
+ __u32 lkey;
+ __u32 rkey;
+ __u32 reserved;
+};
+
+struct ibv_exp_cmd_wq_mp_rq {
+ __u32 use_shift; /* use ibv_exp_mp_rq_shifts */
+ __u8 single_wqe_log_num_of_strides;
+ __u8 single_stride_log_num_of_bytes;
+ __u16 reserved;
+};
+
+enum ibv_exp_cmd_create_wq_comp_mask {
+ IBV_EXP_CMD_CREATE_WQ_MP_RQ = 1 << 0,
+ IBV_EXP_CMD_CREATE_WQ_VLAN_OFFLOADS = 1 << 1,
+ IBV_EXP_CMD_CREATE_WQ_FLAGS = 1 << 2,
+};
+
+struct ibv_exp_create_wq {
+ struct ex_hdr hdr;
+ __u32 comp_mask; /* enum ibv_exp_cmd_create_wq_comp_mask */
+ __u32 wq_type; /* enum ibv_exp_wq_type */
+ __u64 user_handle;
+ __u32 pd_handle;
+ __u32 cq_handle;
+ __u32 srq_handle;
+ __u32 max_recv_wr;
+ __u32 max_recv_sge;
+ __u32 reserved;
+ struct ibv_exp_cmd_wq_mp_rq mp_rq;
+ __u16 wq_vlan_offloads;
+ __u8 reserved1[6];
+ __u64 flags;
+};
+
+struct ibv_exp_create_wq_resp {
+ __u32 comp_mask;
+ __u32 response_length;
+ __u32 wq_handle;
+ __u32 max_recv_wr;
+ __u32 max_recv_sge;
+ __u32 wqn;
+};
+
+struct ib_exp_destroy_wq {
+ struct ex_hdr hdr;
+ __u32 comp_mask;
+ __u32 wq_handle;
+};
+
+struct ib_exp_modify_wq {
+ struct ex_hdr hdr;
+ __u32 comp_mask;
+ __u32 wq_handle;
+ __u32 wq_state;
+ __u32 curr_wq_state;
+ __u16 wq_vlan_offloads;
+ __u8 reserved[6];
+};
+
+struct ibv_exp_create_rwq_ind_table {
+ struct ex_hdr hdr;
+ __u32 comp_mask;
+ __u32 pd_handle;
+ __u32 log_ind_tbl_size;
+ __u32 reserved;
+ /* Following are wq handles based on log_ind_tbl_size, must be 64 bytes aligned.
+ * __u32 wq_handle1
+ * __u32 wq_handle2
+ */
+};
+
+struct ibv_exp_create_rwq_ind_table_resp {
+ __u32 comp_mask;
+ __u32 response_length;
+ __u32 ind_tbl_handle;
+ __u32 ind_tbl_num;
+};
+
+struct ibv_exp_destroy_rwq_ind_table {
+ struct ex_hdr hdr;
+ __u32 comp_mask;
+ __u32 ind_tbl_handle;
+};
+
+struct ibv_exp_kern_ipv6_filter {
+ __u8 src_ip[16];
+ __u8 dst_ip[16];
+};
+
+struct ibv_exp_kern_spec_ipv6 {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ struct ibv_exp_kern_ipv6_filter val;
+ struct ibv_exp_kern_ipv6_filter mask;
+};
+
+struct ibv_exp_kern_ipv6_ext_filter {
+ __u8 src_ip[16];
+ __u8 dst_ip[16];
+ __u32 flow_label;
+ __u8 next_hdr;
+ __u8 traffic_class;
+ __u8 hop_limit;
+ __u8 reserved;
+};
+
+struct ibv_exp_kern_spec_ipv6_ext {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ struct ibv_exp_kern_ipv6_ext_filter val;
+ struct ibv_exp_kern_ipv6_ext_filter mask;
+};
+
+struct ibv_exp_kern_ipv4_ext_filter {
+ __u32 src_ip;
+ __u32 dst_ip;
+ __u8 proto;
+ __u8 tos;
+ __u8 ttl;
+ __u8 flags;
+};
+
+struct ibv_exp_kern_spec_ipv4_ext {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ struct ibv_exp_kern_ipv4_ext_filter val;
+ struct ibv_exp_kern_ipv4_ext_filter mask;
+};
+
+struct ibv_exp_kern_tunnel_filter {
+ __u32 tunnel_id;
+};
+
+struct ibv_exp_kern_spec_tunnel {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ struct ibv_exp_kern_tunnel_filter val;
+ struct ibv_exp_kern_tunnel_filter mask;
+};
+
+struct ibv_exp_kern_spec_action_tag {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ __u32 tag_id;
+ __u32 reserved1;
+};
+
+struct ibv_exp_kern_spec {
+ union {
+ struct {
+ __u32 type;
+ __u16 size;
+ __u16 reserved;
+ } hdr;
+ struct ibv_kern_spec_ib ib;
+ struct ibv_kern_spec_eth eth;
+ struct ibv_kern_spec_ipv4 ipv4;
+ struct ibv_exp_kern_spec_ipv4_ext ipv4_ext;
+ struct ibv_kern_spec_tcp_udp tcp_udp;
+ struct ibv_exp_kern_spec_ipv6 ipv6;
+ struct ibv_exp_kern_spec_ipv6_ext ipv6_ext;
+ struct ibv_exp_kern_spec_tunnel tunnel;
+ struct ibv_exp_kern_spec_action_tag flow_tag;
+ };
+};
+#endif /* KERN_ABI_EXP_H */
diff --git a/external_libs/ibverbs/include/infiniband/marshall.h b/external_libs/ibverbs/include/infiniband/marshall.h
new file mode 100644
index 00000000..8be76c54
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/marshall.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2005 Intel Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INFINIBAND_MARSHALL_H
+#define INFINIBAND_MARSHALL_H
+
+#include <infiniband/verbs.h>
+#include <infiniband/sa.h>
+#include <infiniband/kern-abi.h>
+#include <infiniband/sa-kern-abi.h>
+
+#ifdef __cplusplus
+# define BEGIN_C_DECLS extern "C" {
+# define END_C_DECLS }
+#else /* !__cplusplus */
+# define BEGIN_C_DECLS
+# define END_C_DECLS
+#endif /* __cplusplus */
+
+BEGIN_C_DECLS
+
+void ibv_copy_qp_attr_from_kern(struct ibv_qp_attr *dst,
+ struct ibv_kern_qp_attr *src);
+
+void ibv_copy_ah_attr_from_kern(struct ibv_ah_attr *dst,
+ struct ibv_kern_ah_attr *src);
+
+void ibv_copy_path_rec_from_kern(struct ibv_sa_path_rec *dst,
+ struct ibv_kern_path_rec *src);
+
+void ibv_copy_path_rec_to_kern(struct ibv_kern_path_rec *dst,
+ struct ibv_sa_path_rec *src);
+
+END_C_DECLS
+
+#endif /* INFINIBAND_MARSHALL_H */
diff --git a/external_libs/ibverbs/include/infiniband/mlx5_hw.h b/external_libs/ibverbs/include/infiniband/mlx5_hw.h
new file mode 100644
index 00000000..2ac217d0
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/mlx5_hw.h
@@ -0,0 +1,796 @@
+/**
+ * Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED.
+ * This software product is a proprietary product of Mellanox Technologies Ltd.
+ * (the "Company") and all right, title, and interest and to the software product,
+ * including all associated intellectual property rights, are and shall
+ * remain exclusively with the Company.
+ *
+ * This software product is governed by the End User License Agreement
+ * provided with the software product.
+ */
+
+#ifndef MLX_HW_H_
+#define MLX_HW_H_
+
+#include <linux/types.h>
+#include <stdint.h>
+#include <pthread.h>
+#include <infiniband/driver.h>
+#include <infiniband/verbs.h>
+
+#define MLX5_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#if MLX5_GCC_VERSION >= 403
+# define __MLX5_ALGN_F__ __attribute__((noinline, aligned(64)))
+# define __MLX5_ALGN_D__ __attribute__((aligned(64)))
+#else
+# define __MLX5_ALGN_F__
+# define __MLX5_ALGN_D__
+#endif
+
+#define MLX5_CQ_DB_REQ_NOT_SOL (1 << 24)
+#define MLX5_CQ_DB_REQ_NOT (0 << 24)
+#define MLX5E_CQE_FORMAT_MASK 0xc
+
+enum mlx5_alloc_type { MXM_MLX5_ALLOC_TYPE_DUMMY };
+enum mlx5_rsc_type { MXM_MLX5_RSC_TYPE_DUMMY };
+enum mlx5_db_method { MXM_MLX5_DB_TYPE_DUMMY };
+enum mlx5_lock_type { MXM_MLX5_LOCK_TYPE_DUMMY };
+enum mlx5_lock_state { MXM_MLX5_LOCK_STATE_TYPE_DUMMY };
+enum {
+ MLX5_RCV_DBR = 0,
+ MLX5_SND_DBR = 1,
+ MLX5_SEND_WQE_BB = 64,
+ MLX5_SEND_WQE_SHIFT = 6,
+ MLX5_INLINE_SCATTER_32 = 0x4,
+ MLX5_INLINE_SCATTER_64 = 0x8,
+ MLX5_OPCODE_NOP = 0x00,
+ MLX5_OPCODE_SEND_INVAL = 0x01,
+ MLX5_OPCODE_RDMA_WRITE = 0x08,
+ MLX5_OPCODE_RDMA_WRITE_IMM = 0x09,
+ MLX5_OPCODE_SEND = 0x0a,
+ MLX5_OPCODE_SEND_IMM = 0x0b,
+ MLX5_OPCODE_TSO = 0x0e,
+ MLX5_OPC_MOD_MPW = 0x01,
+ MLX5_OPCODE_LSO_MPW = 0x0e,
+ MLX5_OPCODE_RDMA_READ = 0x10,
+ MLX5_OPCODE_ATOMIC_CS = 0x11,
+ MLX5_OPCODE_ATOMIC_FA = 0x12,
+ MLX5_OPCODE_ATOMIC_MASKED_CS = 0x14,
+ MLX5_OPCODE_ATOMIC_MASKED_FA = 0x15,
+ MLX5_OPCODE_BIND_MW = 0x18,
+ MLX5_OPCODE_FMR = 0x19,
+ MLX5_OPCODE_LOCAL_INVAL = 0x1b,
+ MLX5_OPCODE_CONFIG_CMD = 0x1f,
+ MLX5_OPCODE_SEND_ENABLE = 0x17,
+ MLX5_OPCODE_RECV_ENABLE = 0x16,
+ MLX5_OPCODE_CQE_WAIT = 0x0f,
+ MLX5_RECV_OPCODE_RDMA_WRITE_IMM = 0x00,
+ MLX5_RECV_OPCODE_SEND = 0x01,
+ MLX5_RECV_OPCODE_SEND_IMM = 0x02,
+ MLX5_RECV_OPCODE_SEND_INVAL = 0x03,
+ MLX5_CQE_OPCODE_ERROR = 0x1e,
+ MLX5_CQE_OPCODE_RESIZE = 0x16,
+ MLX5_SRQ_FLAG_SIGNATURE = 1 << 0,
+ MLX5_INLINE_SEG = 0x80000000,
+ MLX5_CALC_UINT64_ADD = 0x01,
+ MLX5_CALC_FLOAT64_ADD = 0x02,
+ MLX5_CALC_UINT64_MAXLOC = 0x03,
+ MLX5_CALC_UINT64_AND = 0x04,
+ MLX5_CALC_UINT64_OR = 0x05,
+ MLX5_CALC_UINT64_XOR = 0x06,
+ MLX5_CQ_DOORBELL = 0x20,
+ MLX5_CQE_SYNDROME_LOCAL_LENGTH_ERR = 0x01,
+ MLX5_CQE_SYNDROME_LOCAL_QP_OP_ERR = 0x02,
+ MLX5_CQE_SYNDROME_LOCAL_PROT_ERR = 0x04,
+ MLX5_CQE_SYNDROME_WR_FLUSH_ERR = 0x05,
+ MLX5_CQE_SYNDROME_MW_BIND_ERR = 0x06,
+ MLX5_CQE_SYNDROME_BAD_RESP_ERR = 0x10,
+ MLX5_CQE_SYNDROME_LOCAL_ACCESS_ERR = 0x11,
+ MLX5_CQE_SYNDROME_REMOTE_INVAL_REQ_ERR = 0x12,
+ MLX5_CQE_SYNDROME_REMOTE_ACCESS_ERR = 0x13,
+ MLX5_CQE_SYNDROME_REMOTE_OP_ERR = 0x14,
+ MLX5_CQE_SYNDROME_TRANSPORT_RETRY_EXC_ERR = 0x15,
+ MLX5_CQE_SYNDROME_RNR_RETRY_EXC_ERR = 0x16,
+ MLX5_CQE_SYNDROME_REMOTE_ABORTED_ERR = 0x22,
+ MLX5_CQE_OWNER_MASK = 1,
+ MLX5_CQE_REQ = 0,
+ MLX5_CQE_RESP_WR_IMM = 1,
+ MLX5_CQE_RESP_SEND = 2,
+ MLX5_CQE_RESP_SEND_IMM = 3,
+ MLX5_CQE_RESP_SEND_INV = 4,
+ MLX5_CQE_RESIZE_CQ = 5,
+ MLX5_CQE_SIG_ERR = 12,
+ MLX5_CQE_REQ_ERR = 13,
+ MLX5_CQE_RESP_ERR = 14,
+ MLX5_CQE_INVALID = 15,
+ MLX5_WQE_CTRL_CQ_UPDATE = 2 << 2,
+ MLX5_WQE_CTRL_SOLICITED = 1 << 1,
+ MLX5_WQE_CTRL_FENCE = 4 << 5,
+ MLX5_INVALID_LKEY = 0x100,
+ MLX5_EXTENDED_UD_AV = 0x80000000,
+ MLX5_NO_INLINE_DATA = 0x0,
+ MLX5_INLINE_DATA32_SEG = 0x1,
+ MLX5_INLINE_DATA64_SEG = 0x2,
+ MLX5_COMPRESSED = 0x3,
+ MLX5_MINI_ARR_SIZE = 8,
+ MLX5_ETH_WQE_L3_CSUM = (1 << 6),
+ MLX5_ETH_WQE_L4_CSUM = (1 << 7),
+ MLX5_ETH_INLINE_HEADER_SIZE = 18,
+ MLX5_ETH_VLAN_INLINE_HEADER_SIZE = 18,
+ MLX5_CQE_L2_OK = 1 << 0,
+ MLX5_CQE_L3_OK = 1 << 1,
+ MLX5_CQE_L4_OK = 1 << 2,
+ MLX5_CQE_L3_HDR_TYPE_NONE = 0x0,
+ MLX5_CQE_L3_HDR_TYPE_IPV6 = 0x4,
+ MLX5_CQE_L3_HDR_TYPE_IPV4 = 0x8,
+ MLX5_CQE_L4_HDR_TYPE_TCP = 0x10,
+ MLX5_CQE_L4_HDR_TYPE_UDP = 0x20,
+ MLX5_CQE_L4_HDR_TYPE_TCP_EMP_ACK = 0x30,
+ MLX5_CQE_L4_HDR_TYPE_TCP_ACK = 0x40,
+ MLX5_CQE_L3_HDR_TYPE_MASK = 0xC,
+ MLX5_CQE_L4_HDR_TYPE_MASK = 0x70,
+ MLX5_QP_PEER_VA_ID_MAX = 2,
+};
+
+struct mlx5_qp;
+
+struct mlx5_resource {
+ enum mlx5_rsc_type type;
+ uint32_t rsn;
+};
+
+
+struct mlx5_wqe_srq_next_seg {
+ uint8_t rsvd0[2];
+ uint16_t next_wqe_index;
+ uint8_t signature;
+ uint8_t rsvd1[11];
+};
+
+
+struct mlx5_wqe_data_seg {
+ uint32_t byte_count;
+ uint32_t lkey;
+ uint64_t addr;
+};
+
+
+struct mlx5_eqe_comp {
+ uint32_t reserved[6];
+ uint32_t cqn;
+};
+
+
+struct mlx5_eqe_qp_srq {
+ uint32_t reserved[6];
+ uint32_t qp_srq_n;
+};
+
+
+struct mlx5_wqe_ctrl_seg {
+ uint32_t opmod_idx_opcode;
+ uint32_t qpn_ds;
+ uint8_t signature;
+ uint8_t rsvd[2];
+ uint8_t fm_ce_se;
+ uint32_t imm;
+};
+
+
+struct mlx5_wqe_xrc_seg {
+ uint32_t xrc_srqn;
+ uint8_t rsvd[12];
+};
+
+
+struct mlx5_wqe_masked_atomic_seg {
+ uint64_t swap_add;
+ uint64_t compare;
+ uint64_t swap_add_mask;
+ uint64_t compare_mask;
+};
+
+
+struct mlx5_base_av {
+ union {
+ struct {
+ uint32_t qkey;
+ uint32_t reserved;
+ } qkey;
+ uint64_t dc_key;
+ } key;
+ uint32_t dqp_dct;
+ uint8_t stat_rate_sl;
+ uint8_t fl_mlid;
+ uint16_t rlid;
+};
+
+
+struct mlx5_grh_av {
+ uint8_t reserved0[4];
+ uint8_t rmac[6];
+ uint8_t tclass;
+ uint8_t hop_limit;
+ uint32_t grh_gid_fl;
+ uint8_t rgid[16];
+};
+
+
+struct mlx5_wqe_av {
+ struct mlx5_base_av base;
+ struct mlx5_grh_av grh_sec;
+};
+
+
+struct mlx5_wqe_datagram_seg {
+ struct mlx5_wqe_av av;
+};
+
+
+struct mlx5_wqe_raddr_seg {
+ uint64_t raddr;
+ uint32_t rkey;
+ uint32_t reserved;
+};
+
+
+struct mlx5_wqe_atomic_seg {
+ uint64_t swap_add;
+ uint64_t compare;
+};
+
+
+struct mlx5_wqe_inl_data_seg {
+ uint32_t byte_count;
+};
+
+
+struct mlx5_wqe_umr_ctrl_seg {
+ uint8_t flags;
+ uint8_t rsvd0[3];
+ uint16_t klm_octowords;
+ uint16_t bsf_octowords;
+ uint64_t mkey_mask;
+ uint8_t rsvd1[32];
+};
+
+
+struct mlx5_seg_set_psv {
+ uint8_t rsvd[4];
+ uint16_t syndrome;
+ uint16_t status;
+ uint16_t block_guard;
+ uint16_t app_tag;
+ uint32_t ref_tag;
+ uint32_t mkey;
+ uint64_t va;
+};
+
+
+struct mlx5_seg_get_psv {
+ uint8_t rsvd[19];
+ uint8_t num_psv;
+ uint32_t l_key;
+ uint64_t va;
+ uint32_t psv_index[4];
+};
+
+
+struct mlx5_seg_check_psv {
+ uint8_t rsvd0[2];
+ uint16_t err_coalescing_op;
+ uint8_t rsvd1[2];
+ uint16_t xport_err_op;
+ uint8_t rsvd2[2];
+ uint16_t xport_err_mask;
+ uint8_t rsvd3[7];
+ uint8_t num_psv;
+ uint32_t l_key;
+ uint64_t va;
+ uint32_t psv_index[4];
+};
+
+
+struct mlx5_rwqe_sig {
+ uint8_t rsvd0[4];
+ uint8_t signature;
+ uint8_t rsvd1[11];
+};
+
+
+struct mlx5_wqe_signature_seg {
+ uint8_t rsvd0[4];
+ uint8_t signature;
+ uint8_t rsvd1[11];
+};
+
+
+struct mlx5_wqe_inline_seg {
+ uint32_t byte_count;
+};
+
+
+struct mlx5_wqe_wait_en_seg {
+ uint8_t rsvd0[8];
+ uint32_t pi;
+ uint32_t obj_num;
+};
+
+
+struct mlx5_err_cqe {
+ uint8_t rsvd0[32];
+ uint32_t srqn;
+ uint8_t rsvd1[16];
+ uint8_t hw_err_synd;
+ uint8_t hw_synd_type;
+ uint8_t vendor_err_synd;
+ uint8_t syndrome;
+ uint32_t s_wqe_opcode_qpn;
+ uint16_t wqe_counter;
+ uint8_t signature;
+ uint8_t op_own;
+};
+
+
+struct mlx5_cqe64 {
+ uint8_t rsvd0[2];
+ /*
+ * wqe_id is valid only for Striding RQ (Multi-Packet RQ).
+ * It provides the WQE index inside the RQ.
+ */
+ uint16_t wqe_id;
+ uint8_t rsvd4[8];
+ uint32_t rx_hash_res;
+ uint8_t rx_hash_type;
+ uint8_t ml_path;
+ uint8_t rsvd20[2];
+ uint16_t checksum;
+ uint16_t slid;
+ uint32_t flags_rqpn;
+ uint8_t hds_ip_ext;
+ uint8_t l4_hdr_type_etc;
+ __be16 vlan_info;
+ uint32_t srqn_uidx;
+ uint32_t imm_inval_pkey;
+ uint8_t rsvd40[4];
+ uint32_t byte_cnt;
+ __be64 timestamp;
+ union {
+ uint32_t sop_drop_qpn;
+ struct {
+ uint8_t sop;
+ uint8_t qpn[3];
+ } sop_qpn;
+ };
+ /*
+ * In Striding RQ (Multi-Packet RQ) wqe_counter provides
+ * the WQE stride index (to calc pointer to start of the message)
+ */
+ uint16_t wqe_counter;
+ uint8_t signature;
+ uint8_t op_own;
+};
+
+
+struct mlx5_spinlock {
+ pthread_spinlock_t lock;
+ enum mlx5_lock_state state;
+};
+
+
+struct mlx5_lock {
+ pthread_mutex_t mutex;
+ pthread_spinlock_t slock;
+ enum mlx5_lock_state state;
+ enum mlx5_lock_type type;
+};
+
+
+struct mlx5_numa_req {
+ int valid;
+ int numa_id;
+};
+
+
+struct mlx5_peer_direct_mem {
+ uint32_t dir;
+ uint64_t va_id;
+ struct ibv_exp_peer_buf *pb;
+ struct ibv_exp_peer_direct_attr *ctx;
+};
+
+
+struct mlx5_buf {
+ void *buf;
+ size_t length;
+ int base;
+ struct mlx5_hugetlb_mem *hmem;
+ struct mlx5_peer_direct_mem peer;
+ enum mlx5_alloc_type type;
+ struct mlx5_numa_req numa_req;
+ int numa_alloc;
+};
+
+
+struct general_data_hot {
+ /* post_send hot data */
+ unsigned *wqe_head;
+ int (*post_send_one)(struct ibv_exp_send_wr *wr,
+ struct mlx5_qp *qp,
+ uint64_t exp_send_flags,
+ void *seg, int *total_size);
+ void *sqstart;
+ void *sqend;
+ volatile uint32_t *db;
+ struct mlx5_bf *bf;
+ uint32_t scur_post;
+ /* Used for burst_family interface, keeps the last posted wqe */
+ uint32_t last_post;
+ uint16_t create_flags;
+ uint8_t fm_cache;
+ uint8_t model_flags; /* use mlx5_qp_model_flags */
+};
+
+
+struct data_seg_data {
+ uint32_t max_inline_data;
+};
+
+
+struct ctrl_seg_data {
+ uint32_t qp_num;
+ uint8_t fm_ce_se_tbl[8];
+ uint8_t fm_ce_se_acc[32];
+ uint8_t wq_sig;
+};
+
+
+struct mpw_data {
+ uint8_t state; /* use mpw_states */
+ uint8_t size;
+ uint8_t num_sge;
+ uint32_t len;
+ uint32_t total_len;
+ uint32_t flags;
+ uint32_t scur_post;
+ union {
+ struct mlx5_wqe_data_seg *last_dseg;
+ uint8_t *inl_data;
+ };
+ uint32_t *ctrl_update;
+};
+
+
+struct general_data_warm {
+ uint32_t pattern;
+ uint8_t qp_type;
+};
+
+
+struct odp_data {
+ struct mlx5_pd *pd;
+};
+
+
+struct mlx5_wq_recv_send_enable {
+ unsigned head_en_index;
+ unsigned head_en_count;
+};
+
+
+struct mlx5_mini_cqe8 {
+ union {
+ uint32_t rx_hash_result;
+ uint32_t checksum;
+ struct {
+ uint16_t wqe_counter;
+ uint8_t s_wqe_opcode;
+ uint8_t reserved;
+ } s_wqe_info;
+ };
+ uint32_t byte_cnt;
+};
+
+
+struct mlx5_cq {
+ struct ibv_cq ibv_cq;
+ uint32_t creation_flags;
+ uint32_t pattern;
+ struct mlx5_buf buf_a;
+ struct mlx5_buf buf_b;
+ struct mlx5_buf *active_buf;
+ struct mlx5_buf *resize_buf;
+ int resize_cqes;
+ int active_cqes;
+ struct mlx5_lock lock;
+ uint32_t cqn;
+ uint32_t cons_index;
+ uint32_t wait_index;
+ uint32_t wait_count;
+ volatile uint32_t *dbrec;
+ int arm_sn;
+ int cqe_sz;
+ int resize_cqe_sz;
+ int stall_next_poll;
+ int stall_enable;
+ uint64_t stall_last_count;
+ int stall_adaptive_enable;
+ int stall_cycles;
+ uint8_t model_flags; /* use mlx5_cq_model_flags */
+ uint16_t cqe_comp_max_num;
+ uint8_t cq_log_size;
+ /* Compressed CQE data */
+ struct mlx5_cqe64 next_decomp_cqe64;
+ struct mlx5_resource *compressed_rsc;
+ uint16_t compressed_left;
+ uint16_t compressed_wqe_cnt;
+ uint8_t compressed_req;
+ uint8_t compressed_mp_rq;
+ uint8_t mini_arr_idx;
+ struct mlx5_mini_cqe8 mini_array[MLX5_MINI_ARR_SIZE];
+ /* peer-direct data */
+ int peer_enabled;
+ struct ibv_exp_peer_direct_attr *peer_ctx;
+ struct mlx5_buf peer_buf;
+ struct mlx5_peek_entry **peer_peek_table;
+ struct mlx5_peek_entry *peer_peek_free;
+};
+
+
+struct mlx5_srq {
+ struct mlx5_resource rsc; /* This struct must be first */
+ struct verbs_srq vsrq;
+ struct mlx5_buf buf;
+ struct mlx5_spinlock lock;
+ uint64_t *wrid;
+ uint32_t srqn;
+ int max;
+ int max_gs;
+ int wqe_shift;
+ int head;
+ int tail;
+ volatile uint32_t *db;
+ uint16_t counter;
+ int wq_sig;
+ struct ibv_srq_legacy *ibv_srq_legacy;
+ int is_xsrq;
+};
+
+
+struct mlx5_wq {
+ /* common hot data */
+ uint64_t *wrid;
+ unsigned wqe_cnt;
+ unsigned head;
+ unsigned tail;
+ unsigned max_post;
+ int max_gs;
+ struct mlx5_lock lock;
+ /* post_recv hot data */
+ void *buff;
+ volatile uint32_t *db;
+ int wqe_shift;
+ int offset;
+};
+
+
+struct mlx5_bf {
+ void *reg;
+ int need_lock;
+ /*
+ * Protect usage of BF address field including data written to the BF
+ * and the BF buffer toggling.
+ */
+ struct mlx5_lock lock;
+ unsigned offset;
+ unsigned buf_size;
+ unsigned uuarn;
+ enum mlx5_db_method db_method;
+};
+
+
+struct mlx5_qp {
+ struct mlx5_resource rsc;
+ struct verbs_qp verbs_qp;
+ struct mlx5_buf buf;
+ int buf_size;
+ /* For Raw Ethernet QP, use different Buffer for the SQ and RQ */
+ struct mlx5_buf sq_buf;
+ int sq_buf_size;
+ uint8_t sq_signal_bits;
+ int umr_en;
+
+ /* hot data used on data path */
+ struct mlx5_wq rq __MLX5_ALGN_D__;
+ struct mlx5_wq sq __MLX5_ALGN_D__;
+
+ struct general_data_hot gen_data;
+ struct mpw_data mpw;
+ struct data_seg_data data_seg;
+ struct ctrl_seg_data ctrl_seg;
+
+ /* RAW_PACKET hot data */
+ uint8_t link_layer;
+
+ /* used on data-path but not so hot */
+ struct general_data_warm gen_data_warm;
+ /* atomic hot data */
+ int enable_atomics;
+ /* odp hot data */
+ struct odp_data odp_data;
+ /* ext atomic hot data */
+ uint32_t max_atomic_arg;
+ /* umr hot data */
+ uint32_t max_inl_send_klms;
+ /* recv-send enable hot data */
+ struct mlx5_wq_recv_send_enable rq_enable;
+ struct mlx5_wq_recv_send_enable sq_enable;
+ int rx_qp;
+ /* peer-direct data */
+ int peer_enabled;
+ struct ibv_exp_peer_direct_attr *peer_ctx;
+ void *peer_ctrl_seg;
+ uint32_t peer_scur_post;
+ uint64_t peer_va_ids[MLX5_QP_PEER_VA_ID_MAX];
+ struct ibv_exp_peer_buf *peer_db_buf;
+ uint32_t max_tso_header;
+};
+
+
+struct mlx5_ah {
+ struct ibv_ah ibv_ah;
+ struct mlx5_wqe_av av;
+};
+
+
+struct mlx5_rwq {
+ struct mlx5_resource rsc;
+ uint32_t pattern;
+ struct ibv_exp_wq wq;
+ struct mlx5_buf buf;
+ int buf_size;
+ /* hot data used on data path */
+ struct mlx5_wq rq __MLX5_ALGN_D__;
+ volatile uint32_t *db;
+ /* Multi-Packet RQ hot data */
+ /* Table to hold the consumed strides on each WQE */
+ uint32_t *consumed_strides_counter;
+ uint16_t mp_rq_stride_size;
+ uint32_t mp_rq_strides_in_wqe;
+ uint8_t mp_rq_packet_padding;
+ /* recv-send enable hot data */
+ struct mlx5_wq_recv_send_enable rq_enable;
+ int wq_sig;
+ uint8_t model_flags; /* use mlx5_wq_model_flags */
+};
+
+
+struct mlx5_wqe_eth_seg {
+ uint32_t rsvd0;
+ uint8_t cs_flags;
+ uint8_t rsvd1;
+ uint16_t mss;
+ uint32_t rsvd2;
+ uint16_t inline_hdr_sz;
+ uint8_t inline_hdr_start[2];
+ uint8_t inline_hdr[16];
+};
+
+
+#define to_mxxx(xxx, type)\
+ ((struct mlx5_##type *)\
+ ((void *) ((uintptr_t)ib##xxx - offsetof(struct mlx5_##type, ibv_##xxx))))
+
+static inline struct mlx5_qp *to_mqp(struct ibv_qp *ibqp)
+{
+ struct verbs_qp *vqp = (struct verbs_qp *)ibqp;
+ return container_of(vqp, struct mlx5_qp, verbs_qp);
+}
+
+static inline struct mlx5_cq *to_mcq(struct ibv_cq *ibcq)
+{
+ return to_mxxx(cq, cq);
+}
+
+struct ibv_mlx5_qp_info {
+ uint32_t qpn;
+ volatile uint32_t *dbrec;
+ struct {
+ void *buf;
+ unsigned wqe_cnt;
+ unsigned stride;
+ } sq, rq;
+ struct {
+ void *reg;
+ unsigned size;
+ int need_lock;
+ } bf;
+};
+
+static inline int ibv_mlx5_exp_get_qp_info(struct ibv_qp *qp, struct ibv_mlx5_qp_info *qp_info)
+{
+ struct mlx5_qp *mqp = to_mqp(qp);
+
+ if ((mqp->gen_data.scur_post != 0) || (mqp->rq.head != 0))
+ return -1;
+
+ qp_info->qpn = mqp->ctrl_seg.qp_num;
+ qp_info->dbrec = mqp->gen_data.db;
+ qp_info->sq.buf = (void *)((uintptr_t)mqp->buf.buf + mqp->sq.offset);
+ qp_info->sq.wqe_cnt = mqp->sq.wqe_cnt;
+ qp_info->sq.stride = 1 << mqp->sq.wqe_shift;
+ qp_info->rq.buf = (void *)((uintptr_t)mqp->buf.buf + mqp->rq.offset);
+ qp_info->rq.wqe_cnt = mqp->rq.wqe_cnt;
+ qp_info->rq.stride = 1 << mqp->rq.wqe_shift;
+ qp_info->bf.reg = mqp->gen_data.bf->reg;
+ qp_info->bf.need_lock = mqp->gen_data.bf->need_lock;
+
+ if (mqp->gen_data.bf->uuarn > 0)
+ qp_info->bf.size = mqp->gen_data.bf->buf_size;
+ else
+ qp_info->bf.size = 0;
+
+ return 0;
+}
+
+struct ibv_mlx5_cq_info {
+ uint32_t cqn;
+ unsigned cqe_cnt;
+ void *buf;
+ volatile uint32_t *dbrec;
+ unsigned cqe_size;
+};
+
+static inline int ibv_mlx5_exp_get_cq_info(struct ibv_cq *cq, struct ibv_mlx5_cq_info *cq_info)
+{
+ struct mlx5_cq *mcq = to_mcq(cq);
+
+ if (mcq->cons_index != 0)
+ return -1;
+
+ cq_info->cqn = mcq->cqn;
+ cq_info->cqe_cnt = mcq->ibv_cq.cqe + 1;
+ cq_info->cqe_size = mcq->cqe_sz;
+ cq_info->buf = mcq->active_buf->buf;
+ cq_info->dbrec = mcq->dbrec;
+
+ return 0;
+}
+
+struct ibv_mlx5_srq_info {
+ void *buf;
+ volatile uint32_t *dbrec;
+ unsigned stride;
+ unsigned head;
+ unsigned tail;
+};
+
+static inline int ibv_mlx5_exp_get_srq_info(struct ibv_srq *srq, struct ibv_mlx5_srq_info *srq_info)
+{
+ struct mlx5_srq *msrq;
+
+ if (srq->handle == LEGACY_XRC_SRQ_HANDLE)
+ srq = (struct ibv_srq *)(((struct ibv_srq_legacy *)srq)->ibv_srq);
+
+ msrq = container_of(srq, struct mlx5_srq, vsrq.srq);
+
+ if (msrq->counter != 0)
+ return -1;
+
+ srq_info->buf = msrq->buf.buf;
+ srq_info->dbrec = msrq->db;
+ srq_info->stride = 1 << msrq->wqe_shift;
+ srq_info->head = msrq->head;
+ srq_info->tail = msrq->tail;
+
+ return 0;
+}
+
+static inline void ibv_mlx5_exp_update_cq_ci(struct ibv_cq *cq, unsigned cq_ci)
+{
+ struct mlx5_cq *mcq = to_mcq(cq);
+
+ mcq->cons_index = cq_ci;
+}
+
+#endif
diff --git a/external_libs/ibverbs/include/infiniband/ofa_verbs.h b/external_libs/ibverbs/include/infiniband/ofa_verbs.h
new file mode 100644
index 00000000..cb0ad62d
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/ofa_verbs.h
@@ -0,0 +1,210 @@
+#ifndef INFINIBAND_OFA_VERBS_H
+#define INFINIBAND_OFA_VERBS_H
+
+struct ibv_srq_init_attr;
+struct ibv_cq;
+struct ibv_pd;
+struct ibv_qp_init_attr;
+struct ibv_qp_attr;
+
+
+#ifdef __GNUC__
+#define DEPRECATED __attribute__((deprecated))
+#else
+#define DEPRECATED
+#endif
+
+/* XRC compatability layer */
+#define LEGACY_XRC_SRQ_HANDLE 0xffffffff
+
+struct ibv_xrc_domain {
+ struct ibv_context *context;
+ uint32_t handle;
+};
+
+struct ibv_srq_legacy {
+ struct ibv_context *context;
+ void *srq_context;
+ struct ibv_pd *pd;
+ uint32_t handle;
+
+ uint32_t events_completed;
+
+ uint32_t xrc_srq_num_bin_compat;
+ struct ibv_xrc_domain *xrc_domain_bin_compat;
+ struct ibv_cq *xrc_cq_bin_compat;
+
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ /* Here we hook the new one from OFED 2.0 */
+ void *ibv_srq;
+ /* Below 3 fields are for legacy source compatibility, reside
+ * on same offset as of those fields in struct ibv_srq.
+ */
+ uint32_t xrc_srq_num;
+ struct ibv_xrc_domain *xrc_domain;
+ struct ibv_cq *xrc_cq;
+};
+
+/**
+ * ibv_open_xrc_domain - open an XRC domain
+ * Returns a reference to an XRC domain.
+ *
+ * @context: Device context
+ * @fd: descriptor for inode associated with the domain
+ * If fd == -1, no inode is associated with the domain; in this ca= se,
+ * the only legal value for oflag is O_CREAT
+ *
+ * @oflag: oflag values are constructed by OR-ing flags from the following list
+ *
+ * O_CREAT
+ * If a domain belonging to device named by context is already associated
+ * with the inode, this flag has no effect, except as noted under O_EXCL
+ * below. Otherwise, a new XRC domain is created and is associated with
+ * inode specified by fd.
+ *
+ * O_EXCL
+ * If O_EXCL and O_CREAT are set, open will fail if a domain associated with
+ * the inode exists. The check for the existence of the domain and creation
+ * of the domain if it does not exist is atomic with respect to other
+ * processes executing open with fd naming the same inode.
+ */
+struct ibv_xrc_domain *ibv_open_xrc_domain(struct ibv_context *context,
+ int fd, int oflag) DEPRECATED;
+
+/**
+ * ibv_create_xrc_srq - Creates a SRQ associated with the specified protection
+ * domain and xrc domain.
+ * @pd: The protection domain associated with the SRQ.
+ * @xrc_domain: The XRC domain associated with the SRQ.
+ * @xrc_cq: CQ to report completions for XRC packets on.
+ *
+ * @srq_init_attr: A list of initial attributes required to create the SRQ.
+ *
+ * srq_attr->max_wr and srq_attr->max_sge are read the determine the
+ * requested size of the SRQ, and set to the actual values allocated
+ * on return. If ibv_create_srq() succeeds, then max_wr and max_sge
+ * will always be at least as large as the requested values.
+ */
+struct ibv_srq *ibv_create_xrc_srq(struct ibv_pd *pd,
+ struct ibv_xrc_domain *xrc_domain,
+ struct ibv_cq *xrc_cq,
+ struct ibv_srq_init_attr *srq_init_attr) DEPRECATED;
+
+/**
+ * ibv_close_xrc_domain - close an XRC domain
+ * If this is the last reference, destroys the domain.
+ *
+ * @d: reference to XRC domain to close
+ *
+ * close is implicitly performed at process exit.
+ */
+int ibv_close_xrc_domain(struct ibv_xrc_domain *d) DEPRECATED;
+
+/**
+ * ibv_create_xrc_rcv_qp - creates an XRC QP for serving as a receive-side-only QP,
+ *
+ * This QP is created in kernel space, and persists until the last process
+ * registered for the QP calls ibv_unreg_xrc_rcv_qp() (at which time the QP
+ * is destroyed).
+ *
+ * @init_attr: init attributes to use for QP. xrc domain MUST be included here.
+ * All other fields are ignored.
+ *
+ * @xrc_rcv_qpn: qp_num of created QP (if success). To be passed to the
+ * remote node (sender). The remote node will use xrc_rcv_qpn
+ * in ibv_post_send when sending to XRC SRQ's on this host
+ * in the same xrc domain.
+ *
+ * RETURNS: success (0), or a (negative) error value.
+ *
+ * NOTE: this verb also registers the calling user-process with the QP at its
+ * creation time (implicit call to ibv_reg_xrc_rcv_qp), to avoid race
+ * conditions. The creating process will need to call ibv_unreg_xrc_qp()
+ * for the QP to release it from this process.
+ */
+int ibv_create_xrc_rcv_qp(struct ibv_qp_init_attr *init_attr,
+ uint32_t *xrc_rcv_qpn) DEPRECATED;
+
+/**
+ * ibv_modify_xrc_rcv_qp - modifies an xrc_rcv qp.
+ *
+ * @xrc_domain: xrc domain the QP belongs to (for verification).
+ * @xrc_qp_num: The (24 bit) number of the XRC QP.
+ * @attr: modify-qp attributes. The following fields must be specified:
+ * for RESET_2_INIT: qp_state, pkey_index , port, qp_access_flags
+ * for INIT_2_RTR: qp_state, path_mtu, dest_qp_num, rq_psn,
+ * max_dest_rd_atomic, min_rnr_timer, ah_attr
+ * The QP need not be brought to RTS for the QP to operate as a
+ * receive-only QP.
+ * @attr_mask: bitmap indicating which attributes are provided in the attr
+ * struct. Used for validity checking.
+ * The following bits must be set:
+ * for RESET_2_INIT: IBV_QP_PKEY_INDEX, IBV_QP_PORT,
+ * IBV_QP_ACCESS_FLAGS, IBV_QP_STATE
+ * for INIT_2_RTR: IBV_QP_AV, IBV_QP_PATH_MTU, IBV_QP_DEST_QPN,
+ * IBV_QP_RQ_PSN, IBV_QP_MAX_DEST_RD_ATOMIC,
+ * IBV_QP_MIN_RNR_TIMER, IBV_QP_STATE
+ *
+ * RETURNS: success (0), or a (positive) error value.
+ *
+ */
+int ibv_modify_xrc_rcv_qp(struct ibv_xrc_domain *xrc_domain,
+ uint32_t xrc_qp_num,
+ struct ibv_qp_attr *attr, int attr_mask) DEPRECATED;
+
+/**
+ * ibv_query_xrc_rcv_qp - queries an xrc_rcv qp.
+ *
+ * @xrc_domain: xrc domain the QP belongs to (for verification).
+ * @xrc_qp_num: The (24 bit) number of the XRC QP.
+ * @attr: for returning qp attributes.
+ * @attr_mask: bitmap indicating which attributes to return.
+ * @init_attr: for returning the init attributes
+ *
+ * RETURNS: success (0), or a (positive) error value.
+ *
+ */
+int ibv_query_xrc_rcv_qp(struct ibv_xrc_domain *xrc_domain, uint32_t xrc_qp_num,
+ struct ibv_qp_attr *attr, int attr_mask,
+ struct ibv_qp_init_attr *init_attr) DEPRECATED;
+
+/**
+ * ibv_reg_xrc_rcv_qp: registers a user process with an XRC QP which serves as
+ * a receive-side only QP.
+ *
+ * @xrc_domain: xrc domain the QP belongs to (for verification).
+ * @xrc_qp_num: The (24 bit) number of the XRC QP.
+ *
+ * RETURNS: success (0),
+ * or error (EINVAL), if:
+ * 1. There is no such QP_num allocated.
+ * 2. The QP is allocated, but is not an receive XRC QP
+ * 3. The XRC QP does not belong to the given domain.
+ */
+int ibv_reg_xrc_rcv_qp(struct ibv_xrc_domain *xrc_domain,
+ uint32_t xrc_qp_num) DEPRECATED;
+
+/**
+ * ibv_unreg_xrc_rcv_qp: detaches a user process from an XRC QP serving as
+ * a receive-side only QP. If as a result, there are no remaining
+ * userspace processes registered for this XRC QP, it is destroyed.
+ *
+ * @xrc_domain: xrc domain the QP belongs to (for verification).
+ * @xrc_qp_num: The (24 bit) number of the XRC QP.
+ *
+ * RETURNS: success (0),
+ * or error (EINVAL), if:
+ * 1. There is no such QP_num allocated.
+ * 2. The QP is allocated, but is not an XRC QP
+ * 3. The XRC QP does not belong to the given domain.
+ * NOTE: There is no reason to return a special code if the QP is destroyed.
+ * The unregister simply succeeds.
+ */
+int ibv_unreg_xrc_rcv_qp(struct ibv_xrc_domain *xrc_domain,
+ uint32_t xrc_qp_num) DEPRECATED;
+
+
+#endif
+
+
diff --git a/external_libs/ibverbs/include/infiniband/opcode.h b/external_libs/ibverbs/include/infiniband/opcode.h
new file mode 100644
index 00000000..fd4bc96a
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/opcode.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2005 Topspin Communications. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INFINIBAND_OPCODE_H
+#define INFINIBAND_OPCODE_H
+
+/*
+ * This macro cleans up the definitions of constants for BTH opcodes.
+ * It is used to define constants such as IBV_OPCODE_UD_SEND_ONLY,
+ * which becomes IBV_OPCODE_UD + IBV_OPCODE_SEND_ONLY, and this gives
+ * the correct value.
+ *
+ * In short, user code should use the constants defined using the
+ * macro rather than worrying about adding together other constants.
+*/
+#define IBV_OPCODE(transport, op) \
+ IBV_OPCODE_ ## transport ## _ ## op = \
+ IBV_OPCODE_ ## transport + IBV_OPCODE_ ## op
+
+enum {
+ /* transport types -- just used to define real constants */
+ IBV_OPCODE_RC = 0x00,
+ IBV_OPCODE_UC = 0x20,
+ IBV_OPCODE_RD = 0x40,
+ IBV_OPCODE_UD = 0x60,
+
+ /* operations -- just used to define real constants */
+ IBV_OPCODE_SEND_FIRST = 0x00,
+ IBV_OPCODE_SEND_MIDDLE = 0x01,
+ IBV_OPCODE_SEND_LAST = 0x02,
+ IBV_OPCODE_SEND_LAST_WITH_IMMEDIATE = 0x03,
+ IBV_OPCODE_SEND_ONLY = 0x04,
+ IBV_OPCODE_SEND_ONLY_WITH_IMMEDIATE = 0x05,
+ IBV_OPCODE_RDMA_WRITE_FIRST = 0x06,
+ IBV_OPCODE_RDMA_WRITE_MIDDLE = 0x07,
+ IBV_OPCODE_RDMA_WRITE_LAST = 0x08,
+ IBV_OPCODE_RDMA_WRITE_LAST_WITH_IMMEDIATE = 0x09,
+ IBV_OPCODE_RDMA_WRITE_ONLY = 0x0a,
+ IBV_OPCODE_RDMA_WRITE_ONLY_WITH_IMMEDIATE = 0x0b,
+ IBV_OPCODE_RDMA_READ_REQUEST = 0x0c,
+ IBV_OPCODE_RDMA_READ_RESPONSE_FIRST = 0x0d,
+ IBV_OPCODE_RDMA_READ_RESPONSE_MIDDLE = 0x0e,
+ IBV_OPCODE_RDMA_READ_RESPONSE_LAST = 0x0f,
+ IBV_OPCODE_RDMA_READ_RESPONSE_ONLY = 0x10,
+ IBV_OPCODE_ACKNOWLEDGE = 0x11,
+ IBV_OPCODE_ATOMIC_ACKNOWLEDGE = 0x12,
+ IBV_OPCODE_COMPARE_SWAP = 0x13,
+ IBV_OPCODE_FETCH_ADD = 0x14,
+
+ /* real constants follow -- see comment about above IBV_OPCODE()
+ macro for more details */
+
+ /* RC */
+ IBV_OPCODE(RC, SEND_FIRST),
+ IBV_OPCODE(RC, SEND_MIDDLE),
+ IBV_OPCODE(RC, SEND_LAST),
+ IBV_OPCODE(RC, SEND_LAST_WITH_IMMEDIATE),
+ IBV_OPCODE(RC, SEND_ONLY),
+ IBV_OPCODE(RC, SEND_ONLY_WITH_IMMEDIATE),
+ IBV_OPCODE(RC, RDMA_WRITE_FIRST),
+ IBV_OPCODE(RC, RDMA_WRITE_MIDDLE),
+ IBV_OPCODE(RC, RDMA_WRITE_LAST),
+ IBV_OPCODE(RC, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+ IBV_OPCODE(RC, RDMA_WRITE_ONLY),
+ IBV_OPCODE(RC, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+ IBV_OPCODE(RC, RDMA_READ_REQUEST),
+ IBV_OPCODE(RC, RDMA_READ_RESPONSE_FIRST),
+ IBV_OPCODE(RC, RDMA_READ_RESPONSE_MIDDLE),
+ IBV_OPCODE(RC, RDMA_READ_RESPONSE_LAST),
+ IBV_OPCODE(RC, RDMA_READ_RESPONSE_ONLY),
+ IBV_OPCODE(RC, ACKNOWLEDGE),
+ IBV_OPCODE(RC, ATOMIC_ACKNOWLEDGE),
+ IBV_OPCODE(RC, COMPARE_SWAP),
+ IBV_OPCODE(RC, FETCH_ADD),
+
+ /* UC */
+ IBV_OPCODE(UC, SEND_FIRST),
+ IBV_OPCODE(UC, SEND_MIDDLE),
+ IBV_OPCODE(UC, SEND_LAST),
+ IBV_OPCODE(UC, SEND_LAST_WITH_IMMEDIATE),
+ IBV_OPCODE(UC, SEND_ONLY),
+ IBV_OPCODE(UC, SEND_ONLY_WITH_IMMEDIATE),
+ IBV_OPCODE(UC, RDMA_WRITE_FIRST),
+ IBV_OPCODE(UC, RDMA_WRITE_MIDDLE),
+ IBV_OPCODE(UC, RDMA_WRITE_LAST),
+ IBV_OPCODE(UC, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+ IBV_OPCODE(UC, RDMA_WRITE_ONLY),
+ IBV_OPCODE(UC, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+
+ /* RD */
+ IBV_OPCODE(RD, SEND_FIRST),
+ IBV_OPCODE(RD, SEND_MIDDLE),
+ IBV_OPCODE(RD, SEND_LAST),
+ IBV_OPCODE(RD, SEND_LAST_WITH_IMMEDIATE),
+ IBV_OPCODE(RD, SEND_ONLY),
+ IBV_OPCODE(RD, SEND_ONLY_WITH_IMMEDIATE),
+ IBV_OPCODE(RD, RDMA_WRITE_FIRST),
+ IBV_OPCODE(RD, RDMA_WRITE_MIDDLE),
+ IBV_OPCODE(RD, RDMA_WRITE_LAST),
+ IBV_OPCODE(RD, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+ IBV_OPCODE(RD, RDMA_WRITE_ONLY),
+ IBV_OPCODE(RD, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+ IBV_OPCODE(RD, RDMA_READ_REQUEST),
+ IBV_OPCODE(RD, RDMA_READ_RESPONSE_FIRST),
+ IBV_OPCODE(RD, RDMA_READ_RESPONSE_MIDDLE),
+ IBV_OPCODE(RD, RDMA_READ_RESPONSE_LAST),
+ IBV_OPCODE(RD, RDMA_READ_RESPONSE_ONLY),
+ IBV_OPCODE(RD, ACKNOWLEDGE),
+ IBV_OPCODE(RD, ATOMIC_ACKNOWLEDGE),
+ IBV_OPCODE(RD, COMPARE_SWAP),
+ IBV_OPCODE(RD, FETCH_ADD),
+
+ /* UD */
+ IBV_OPCODE(UD, SEND_ONLY),
+ IBV_OPCODE(UD, SEND_ONLY_WITH_IMMEDIATE)
+};
+
+#endif /* INFINIBAND_OPCODE_H */
diff --git a/external_libs/ibverbs/include/infiniband/peer_ops.h b/external_libs/ibverbs/include/infiniband/peer_ops.h
new file mode 100644
index 00000000..d2fd265d
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/peer_ops.h
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2016 Mellanox Technologies Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef PEER_OPS_H
+#define PEER_OPS_H
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <infiniband/verbs.h>
+
+BEGIN_C_DECLS
+
+enum ibv_exp_peer_op {
+ IBV_EXP_PEER_OP_RESERVED1 = 1,
+
+ IBV_EXP_PEER_OP_FENCE = 0,
+
+ IBV_EXP_PEER_OP_STORE_DWORD = 4,
+ IBV_EXP_PEER_OP_STORE_QWORD = 2,
+ IBV_EXP_PEER_OP_COPY_BLOCK = 3,
+
+ IBV_EXP_PEER_OP_POLL_AND_DWORD = 12,
+ IBV_EXP_PEER_OP_POLL_NOR_DWORD = 13,
+ IBV_EXP_PEER_OP_POLL_GEQ_DWORD = 14,
+};
+
+enum ibv_exp_peer_op_caps {
+ IBV_EXP_PEER_OP_FENCE_CAP = (1 << IBV_EXP_PEER_OP_FENCE),
+ IBV_EXP_PEER_OP_STORE_DWORD_CAP = (1 << IBV_EXP_PEER_OP_STORE_DWORD),
+ IBV_EXP_PEER_OP_STORE_QWORD_CAP = (1 << IBV_EXP_PEER_OP_STORE_QWORD),
+ IBV_EXP_PEER_OP_COPY_BLOCK_CAP = (1 << IBV_EXP_PEER_OP_COPY_BLOCK),
+ IBV_EXP_PEER_OP_POLL_AND_DWORD_CAP
+ = (1 << IBV_EXP_PEER_OP_POLL_AND_DWORD),
+ IBV_EXP_PEER_OP_POLL_NOR_DWORD_CAP
+ = (1 << IBV_EXP_PEER_OP_POLL_NOR_DWORD),
+ IBV_EXP_PEER_OP_POLL_GEQ_DWORD_CAP
+ = (1 << IBV_EXP_PEER_OP_POLL_GEQ_DWORD),
+};
+
+enum ibv_exp_peer_fence {
+ IBV_EXP_PEER_FENCE_OP_READ = (1 << 0),
+ IBV_EXP_PEER_FENCE_OP_WRITE = (1 << 1),
+ IBV_EXP_PEER_FENCE_FROM_CPU = (1 << 2),
+ IBV_EXP_PEER_FENCE_FROM_HCA = (1 << 3),
+ IBV_EXP_PEER_FENCE_MEM_SYS = (1 << 4),
+ IBV_EXP_PEER_FENCE_MEM_PEER = (1 << 5),
+};
+
+/* Indicate HW entities supposed to access memory buffer:
+ * IBV_EXP_PEER_DIRECTION_FROM_X means X writes to the buffer
+ * IBV_EXP_PEER_DIRECTION_TO_Y means Y read from the buffer
+ */
+enum ibv_exp_peer_direction {
+ IBV_EXP_PEER_DIRECTION_FROM_CPU = (1 << 0),
+ IBV_EXP_PEER_DIRECTION_FROM_HCA = (1 << 1),
+ IBV_EXP_PEER_DIRECTION_FROM_PEER = (1 << 2),
+ IBV_EXP_PEER_DIRECTION_TO_CPU = (1 << 3),
+ IBV_EXP_PEER_DIRECTION_TO_HCA = (1 << 4),
+ IBV_EXP_PEER_DIRECTION_TO_PEER = (1 << 5),
+};
+
+struct ibv_exp_peer_buf_alloc_attr {
+ size_t length;
+ /* Bitmask from enum ibv_exp_peer_direction */
+ uint32_t dir;
+ /* The ID of the peer device which will be
+ * accessing the allocated buffer
+ */
+ uint64_t peer_id;
+ /* Data alignment */
+ uint32_t alignment;
+ /* Reserved for future extensions, must be 0 */
+ uint32_t comp_mask;
+};
+
+struct ibv_exp_peer_buf {
+ void *addr;
+ size_t length;
+ /* Reserved for future extensions, must be 0 */
+ uint32_t comp_mask;
+};
+
+enum ibv_exp_peer_direct_attr_mask {
+ IBV_EXP_PEER_DIRECT_VERSION = (1 << 0) /* Must be set */
+};
+
+#define IBV_EXP_PEER_IOMEMORY ((struct ibv_exp_peer_buf *)-1UL)
+
+struct ibv_exp_peer_direct_attr {
+ /* Unique ID per peer device.
+ * Used to identify specific HW devices where relevant.
+ */
+ uint64_t peer_id;
+ /* buf_alloc callback should return struct ibv_exp_peer_buf with buffer
+ * of at least attr->length.
+ * @attr: description of desired buffer
+ *
+ * Buffer should be mapped in the application address space
+ * for read/write (depends on attr->dir value).
+ * attr->dir value is supposed to indicate the expected directions
+ * of access to the buffer, to allow optimization by the peer driver.
+ * If NULL returned then buffer will be allocated in system memory
+ * by ibverbs driver.
+ */
+ struct ibv_exp_peer_buf *(*buf_alloc)(struct ibv_exp_peer_buf_alloc_attr *attr);
+ /* If buffer was allocated by buf_alloc then buf_release will be
+ * called to release it.
+ * @pb: struct returned by buf_alloc
+ *
+ * buf_release is responsible to release everything allocated by
+ * buf_alloc.
+ * Return 0 on succes.
+ */
+ int (*buf_release)(struct ibv_exp_peer_buf *pb);
+ /* register_va callback should register virtual address from the
+ * application as an area the peer is allowed to access.
+ * @start: pointer to beginning of region in virtual space
+ * @length: length of region
+ * @peer_id: the ID of the peer device which will be accessing
+ * the region.
+ * @pb: if registering a buffer that was returned from buf_alloc(),
+ * pb is the struct that was returned. If registering io memory area,
+ * pb is IBV_EXP_PEER_IOMEMORY. Otherwise - NULL
+ *
+ * Return id of registered address on success, 0 on failure.
+ */
+ uint64_t (*register_va)(void *start, size_t length, uint64_t peer_id,
+ struct ibv_exp_peer_buf *pb);
+ /* If virtual address was registered with register_va then
+ * unregister_va will be called to unregister it.
+ * @target_id: id returned by register_va
+ * @peer_id: the ID of the peer device passed to register_va
+ *
+ * Return 0 on success.
+ */
+ int (*unregister_va)(uint64_t target_id, uint64_t peer_id);
+ /* Bitmask from ibv_exp_peer_op_caps */
+ uint64_t caps;
+ /* Maximal length of DMA operation the peer can do in copy-block */
+ size_t peer_dma_op_map_len;
+ /* From ibv_exp_peer_direct_attr_mask */
+ uint32_t comp_mask;
+ /* Feature version, must be 1 */
+ uint32_t version;
+};
+
+/* QP API - CPU posts send work-requests without exposing them to the HW.
+ * Later, the peer device exposes the relevant work requests to the HCA
+ * for execution.
+ */
+
+struct peer_op_wr {
+ struct peer_op_wr *next;
+ enum ibv_exp_peer_op type;
+ union {
+ struct {
+ uint64_t fence_flags; /* from ibv_exp_peer_fence */
+ } fence;
+
+ struct {
+ uint32_t data;
+ uint64_t target_id;
+ size_t offset;
+ } dword_va; /* Use for all operations targeting dword */
+
+ struct {
+ uint64_t data;
+ uint64_t target_id;
+ size_t offset;
+ } qword_va; /* Use for all operations targeting qword */
+
+ struct {
+ void *src;
+ uint64_t target_id;
+ size_t offset;
+ size_t len;
+ } copy_op;
+ } wr;
+ uint32_t comp_mask; /* Reserved for future expensions, must be 0 */
+};
+
+struct ibv_exp_peer_commit {
+ /* IN/OUT - linked list of empty/filled descriptors */
+ struct peer_op_wr *storage;
+ /* IN/OUT - number of allocated/filled descriptors */
+ uint32_t entries;
+ /* OUT - identifier used in ibv_exp_rollback_qp to rollback WQEs set */
+ uint64_t rollback_id;
+ uint32_t comp_mask; /* Reserved for future expensions, must be 0 */
+};
+
+/**
+ * ibv_exp_peer_commit_qp - request descriptors for committing all WQEs
+ * currently posted to the send work queue
+ * @qp: the QP being requested
+ * @peer: context with list of &struct peer_op_wr describing actions
+ * necessary to commit WQEs
+ *
+ * Function
+ * - fill peer->storage with descriptors
+ * - put number of filled descriptors to peer->entries;
+ * - put data necessary for rollback to peer->rollback_id
+ * If number of entries is not sufficient - return -ENOSPC
+ *
+ * Note: caller is responsible to ensure that the peer fences any data store
+ * before executing the commit
+ */
+static inline int ibv_exp_peer_commit_qp(struct ibv_qp *qp,
+ struct ibv_exp_peer_commit *peer)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(qp->context, exp_peer_commit_qp);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->exp_peer_commit_qp(qp, peer);
+}
+
+enum ibv_exp_rollback_flags {
+ /* Abort all WQEs which were not committed to HW yet.
+ * rollback_id is ignored. **/
+ IBV_EXP_ROLLBACK_ABORT_UNCOMMITED = (1 << 0),
+ /* Abort the request even if there are following requests
+ * being aborted as well. **/
+ IBV_EXP_ROLLBACK_ABORT_LATE = (1 << 1),
+};
+
+struct ibv_exp_rollback_ctx {
+ uint64_t rollback_id; /* from ibv_exp_peer_commit call */
+ uint32_t flags; /* from ibv_exp_rollback_flags */
+ uint32_t comp_mask; /* Reserved for future expensions, must be 0 */
+};
+
+/**
+ * ibv_exp_rollback_qp - indicate that the commit attempt failed
+ * @qp: the QP being rolled back
+ * @rollback: context with rollback_id returned by
+ * earlier ibv_exp_peer_commit_qp and flags
+ */
+static inline int ibv_exp_rollback_qp(struct ibv_qp *qp,
+ struct ibv_exp_rollback_ctx *rollback)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(qp->context, exp_rollback_send);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->exp_rollback_send(qp, rollback);
+}
+
+/* CQ interface - peek into a CQ and describe how to check if
+ * there is a CQ entry available.
+ */
+
+enum {
+ IBV_EXP_PEER_PEEK_ABSOLUTE,
+ IBV_EXP_PEER_PEEK_RELATIVE
+};
+
+struct ibv_exp_peer_peek {
+ /* IN/OUT - linked list of empty/filled descriptors */
+ struct peer_op_wr *storage;
+ /* IN/OUT - number of allocated/filled descriptors */
+ uint32_t entries;
+ /* IN - Which CQ entry does the peer want to peek for
+ * completion. According to "whence" directive entry
+ * chosen as follows:
+ * IBV_EXP_PEER_PEEK_ABSOLUTE -
+ * "offset" is absolute index of entry wrapped to 32-bit
+ * IBV_EXP_PEER_PEEK_RELATIVE -
+ * "offset" is relative to current poll_cq location.
+ */
+ uint32_t whence;
+ uint32_t offset;
+ /* OUT - identifier used in ibv_exp_peer_ack_peek_cq to advance CQ */
+ uint64_t peek_id;
+ uint32_t comp_mask; /* Reserved for future expensions, must be 0 */
+};
+
+/**
+ * ibv_exp_peer_peek_cq - request descriptors for peeking CQ in specific
+ * offset from currently expected CQ entry.
+ * @cq: the CQ being requested
+ * @peer_ctx: context with list of &struct peer_op_wr describing actions
+ * necessary to wait for desired CQ entry is delivered and report
+ * this to ibverbs.
+ *
+ * A peek CQ request places a "block" on the relevant CQ entry.
+ * Poll CQ requests to poll the CQ entry will fail with an error.
+ * The block will be removed by executing the descriptors.
+ * If the peer will not be able to execute the descriptors,
+ * it should call ibv_exp_peer_abort_peek_cq to remove the block.
+ *
+ * Function
+ * - fill peek_ctx->storage with descriptors.
+ * - put number of filled descriptors to peek_ctx->entries.
+ * - put data necessary to abort peek.
+ * If number of entries is not sufficient - return -ENOSPC.
+ */
+static inline int ibv_exp_peer_peek_cq(struct ibv_cq *cq,
+ struct ibv_exp_peer_peek *peek_ctx)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(cq->context, exp_peer_peek_cq);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->exp_peer_peek_cq(cq, peek_ctx);
+}
+
+struct ibv_exp_peer_abort_peek {
+ uint64_t peek_id; /* From the peer_peek_cq call */
+ uint32_t comp_mask; /* Reserved for future expensions, must be 0 */
+};
+
+/**
+ * ibv_exp_peer_abort_peek_cq - indicate that peek is aborted
+ * @cq: the CQ being rolled back
+ * @abort_ctx: context with peek_id returned by earlier ibv_exp_peer_peek_cq
+ *
+ * Note: This should be done only if the peek descriptors were not executed
+ */
+static inline int ibv_exp_peer_abort_peek_cq(struct ibv_cq *cq,
+ struct ibv_exp_peer_abort_peek *abort_ctx)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(cq->context, exp_peer_abort_peek_cq);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->exp_peer_abort_peek_cq(cq, abort_ctx);
+}
+
+END_C_DECLS
+
+#endif /* PEER_OPS_H */
diff --git a/external_libs/ibverbs/include/infiniband/sa-kern-abi.h b/external_libs/ibverbs/include/infiniband/sa-kern-abi.h
new file mode 100644
index 00000000..4927d114
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/sa-kern-abi.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2005 Intel Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INFINIBAND_SA_KERN_ABI_H
+#define INFINIBAND_SA_KERN_ABI_H
+
+#include <linux/types.h>
+
+/*
+ * Obsolete, deprecated names. Will be removed in libibverbs 1.1.
+ */
+#define ib_kern_path_rec ibv_kern_path_rec
+
+struct ibv_kern_path_rec {
+ __u8 dgid[16];
+ __u8 sgid[16];
+ __u16 dlid;
+ __u16 slid;
+ __u32 raw_traffic;
+ __u32 flow_label;
+ __u32 reversible;
+ __u32 mtu;
+ __u16 pkey;
+ __u8 hop_limit;
+ __u8 traffic_class;
+ __u8 numb_path;
+ __u8 sl;
+ __u8 mtu_selector;
+ __u8 rate_selector;
+ __u8 rate;
+ __u8 packet_life_time_selector;
+ __u8 packet_life_time;
+ __u8 preference;
+};
+
+#endif /* INFINIBAND_SA_KERN_ABI_H */
diff --git a/external_libs/ibverbs/include/infiniband/sa.h b/external_libs/ibverbs/include/infiniband/sa.h
new file mode 100644
index 00000000..1153e943
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/sa.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2004 Topspin Communications. All rights reserved.
+ * Copyright (c) 2005 Voltaire, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INFINIBAND_SA_H
+#define INFINIBAND_SA_H
+
+#include <infiniband/verbs.h>
+
+struct ibv_sa_path_rec {
+ /* reserved */
+ /* reserved */
+ union ibv_gid dgid;
+ union ibv_gid sgid;
+ uint16_t dlid;
+ uint16_t slid;
+ int raw_traffic;
+ /* reserved */
+ uint32_t flow_label;
+ uint8_t hop_limit;
+ uint8_t traffic_class;
+ int reversible;
+ uint8_t numb_path;
+ uint16_t pkey;
+ /* reserved */
+ uint8_t sl;
+ uint8_t mtu_selector;
+ uint8_t mtu;
+ uint8_t rate_selector;
+ uint8_t rate;
+ uint8_t packet_life_time_selector;
+ uint8_t packet_life_time;
+ uint8_t preference;
+};
+
+struct ibv_sa_mcmember_rec {
+ union ibv_gid mgid;
+ union ibv_gid port_gid;
+ uint32_t qkey;
+ uint16_t mlid;
+ uint8_t mtu_selector;
+ uint8_t mtu;
+ uint8_t traffic_class;
+ uint16_t pkey;
+ uint8_t rate_selector;
+ uint8_t rate;
+ uint8_t packet_life_time_selector;
+ uint8_t packet_life_time;
+ uint8_t sl;
+ uint32_t flow_label;
+ uint8_t hop_limit;
+ uint8_t scope;
+ uint8_t join_state;
+ int proxy_join;
+};
+
+struct ibv_sa_service_rec {
+ uint64_t id;
+ union ibv_gid gid;
+ uint16_t pkey;
+ /* uint16_t resv; */
+ uint32_t lease;
+ uint8_t key[16];
+ uint8_t name[64];
+ uint8_t data8[16];
+ uint16_t data16[8];
+ uint32_t data32[4];
+ uint64_t data64[2];
+};
+
+#define IBV_PATH_RECORD_REVERSIBLE 0x80
+
+struct ibv_path_record {
+ uint64_t service_id;
+ union ibv_gid dgid;
+ union ibv_gid sgid;
+ uint16_t dlid;
+ uint16_t slid;
+ uint32_t flowlabel_hoplimit; /* resv-31:28 flow label-27:8 hop limit-7:0*/
+ uint8_t tclass;
+ uint8_t reversible_numpath; /* reversible-7:7 num path-6:0 */
+ uint16_t pkey;
+ uint16_t qosclass_sl; /* qos class-15:4 sl-3:0 */
+ uint8_t mtu; /* mtu selector-7:6 mtu-5:0 */
+ uint8_t rate; /* rate selector-7:6 rate-5:0 */
+ uint8_t packetlifetime; /* lifetime selector-7:6 lifetime-5:0 */
+ uint8_t preference;
+ uint8_t reserved[6];
+};
+
+#define IBV_PATH_FLAG_GMP (1<<0)
+#define IBV_PATH_FLAG_PRIMARY (1<<1)
+#define IBV_PATH_FLAG_ALTERNATE (1<<2)
+#define IBV_PATH_FLAG_OUTBOUND (1<<3)
+#define IBV_PATH_FLAG_INBOUND (1<<4)
+#define IBV_PATH_FLAG_INBOUND_REVERSE (1<<5)
+#define IBV_PATH_FLAG_BIDIRECTIONAL (IBV_PATH_FLAG_OUTBOUND | \
+ IBV_PATH_FLAG_INBOUND_REVERSE)
+
+struct ibv_path_data {
+ uint32_t flags;
+ uint32_t reserved;
+ struct ibv_path_record path;
+};
+
+#endif /* INFINIBAND_SA_H */
diff --git a/external_libs/ibverbs/include/infiniband/verbs.h b/external_libs/ibverbs/include/infiniband/verbs.h
new file mode 100644
index 00000000..6a8d7cc1
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/verbs.h
@@ -0,0 +1,1642 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2004, 2011-2012 Intel Corporation. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2005 PathScale, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INFINIBAND_VERBS_H
+#define INFINIBAND_VERBS_H
+
+#include <stdint.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <errno.h>
+#include <infiniband/ofa_verbs.h>
+
+#ifdef __cplusplus
+# define BEGIN_C_DECLS extern "C" {
+# define END_C_DECLS }
+#else /* !__cplusplus */
+# define BEGIN_C_DECLS
+# define END_C_DECLS
+#endif /* __cplusplus */
+
+#if __GNUC__ >= 3
+# define __attribute_const __attribute__((const))
+#else
+# define __attribute_const
+#endif
+
+BEGIN_C_DECLS
+
+union ibv_gid {
+ uint8_t raw[16];
+ struct {
+ uint64_t subnet_prefix;
+ uint64_t interface_id;
+ } global;
+};
+
+#ifndef container_of
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) \
+ ((type *) ((uint8_t *)(ptr) - offsetof(type, member)))
+#endif
+
+#define vext_field_avail(type, fld, sz) (offsetof(type, fld) < (sz))
+
+static void *__VERBS_ABI_IS_EXTENDED = ((uint8_t *)NULL) - 1;
+
+enum ibv_node_type {
+ IBV_NODE_UNKNOWN = -1,
+ IBV_NODE_CA = 1,
+ IBV_NODE_SWITCH,
+ IBV_NODE_ROUTER,
+ IBV_NODE_RNIC,
+
+ /* Leave a gap for future node types before starting with
+ * experimental node types.
+ */
+ IBV_EXP_NODE_TYPE_START = 32,
+ IBV_EXP_NODE_MIC = IBV_EXP_NODE_TYPE_START
+};
+
+enum ibv_transport_type {
+ IBV_TRANSPORT_UNKNOWN = -1,
+ IBV_TRANSPORT_IB = 0,
+ IBV_TRANSPORT_IWARP,
+
+ /* Leave a gap for future transport types before starting with
+ * experimental transport types.
+ */
+ IBV_EXP_TRANSPORT_TYPE_START = 32,
+ IBV_EXP_TRANSPORT_SCIF = IBV_EXP_TRANSPORT_TYPE_START
+};
+
+enum ibv_device_cap_flags {
+ IBV_DEVICE_RESIZE_MAX_WR = 1,
+ IBV_DEVICE_BAD_PKEY_CNTR = 1 << 1,
+ IBV_DEVICE_BAD_QKEY_CNTR = 1 << 2,
+ IBV_DEVICE_RAW_MULTI = 1 << 3,
+ IBV_DEVICE_AUTO_PATH_MIG = 1 << 4,
+ IBV_DEVICE_CHANGE_PHY_PORT = 1 << 5,
+ IBV_DEVICE_UD_AV_PORT_ENFORCE = 1 << 6,
+ IBV_DEVICE_CURR_QP_STATE_MOD = 1 << 7,
+ IBV_DEVICE_SHUTDOWN_PORT = 1 << 8,
+ IBV_DEVICE_INIT_TYPE = 1 << 9,
+ IBV_DEVICE_PORT_ACTIVE_EVENT = 1 << 10,
+ IBV_DEVICE_SYS_IMAGE_GUID = 1 << 11,
+ IBV_DEVICE_RC_RNR_NAK_GEN = 1 << 12,
+ IBV_DEVICE_SRQ_RESIZE = 1 << 13,
+ IBV_DEVICE_N_NOTIFY_CQ = 1 << 14,
+ IBV_DEVICE_XRC = 1 << 20,
+ IBV_DEVICE_MANAGED_FLOW_STEERING = 1 << 29
+};
+
+enum ibv_atomic_cap {
+ IBV_ATOMIC_NONE,
+ IBV_ATOMIC_HCA,
+ IBV_ATOMIC_GLOB
+};
+
+struct ibv_device_attr {
+ char fw_ver[64];
+ uint64_t node_guid;
+ uint64_t sys_image_guid;
+ uint64_t max_mr_size;
+ uint64_t page_size_cap;
+ uint32_t vendor_id;
+ uint32_t vendor_part_id;
+ uint32_t hw_ver;
+ int max_qp;
+ int max_qp_wr;
+ int device_cap_flags;
+ int max_sge;
+ int max_sge_rd;
+ int max_cq;
+ int max_cqe;
+ int max_mr;
+ int max_pd;
+ int max_qp_rd_atom;
+ int max_ee_rd_atom;
+ int max_res_rd_atom;
+ int max_qp_init_rd_atom;
+ int max_ee_init_rd_atom;
+ enum ibv_atomic_cap atomic_cap;
+ int max_ee;
+ int max_rdd;
+ int max_mw;
+ int max_raw_ipv6_qp;
+ int max_raw_ethy_qp;
+ int max_mcast_grp;
+ int max_mcast_qp_attach;
+ int max_total_mcast_qp_attach;
+ int max_ah;
+ int max_fmr;
+ int max_map_per_fmr;
+ int max_srq;
+ int max_srq_wr;
+ int max_srq_sge;
+ uint16_t max_pkeys;
+ uint8_t local_ca_ack_delay;
+ uint8_t phys_port_cnt;
+};
+
+enum ibv_mtu {
+ IBV_MTU_256 = 1,
+ IBV_MTU_512 = 2,
+ IBV_MTU_1024 = 3,
+ IBV_MTU_2048 = 4,
+ IBV_MTU_4096 = 5
+};
+
+enum ibv_port_state {
+ IBV_PORT_NOP = 0,
+ IBV_PORT_DOWN = 1,
+ IBV_PORT_INIT = 2,
+ IBV_PORT_ARMED = 3,
+ IBV_PORT_ACTIVE = 4,
+ IBV_PORT_ACTIVE_DEFER = 5
+};
+
+enum {
+ IBV_LINK_LAYER_UNSPECIFIED,
+ IBV_LINK_LAYER_INFINIBAND,
+ IBV_LINK_LAYER_ETHERNET,
+
+ /* Leave a gap for future link layer types before starting with
+ * experimental link layer.
+ */
+ IBV_EXP_LINK_LAYER_START = 32,
+ IBV_EXP_LINK_LAYER_SCIF = IBV_EXP_LINK_LAYER_START
+};
+
+enum ibv_port_cap_flags {
+ IBV_PORT_SM = 1 << 1,
+ IBV_PORT_NOTICE_SUP = 1 << 2,
+ IBV_PORT_TRAP_SUP = 1 << 3,
+ IBV_PORT_OPT_IPD_SUP = 1 << 4,
+ IBV_PORT_AUTO_MIGR_SUP = 1 << 5,
+ IBV_PORT_SL_MAP_SUP = 1 << 6,
+ IBV_PORT_MKEY_NVRAM = 1 << 7,
+ IBV_PORT_PKEY_NVRAM = 1 << 8,
+ IBV_PORT_LED_INFO_SUP = 1 << 9,
+ IBV_PORT_SYS_IMAGE_GUID_SUP = 1 << 11,
+ IBV_PORT_PKEY_SW_EXT_PORT_TRAP_SUP = 1 << 12,
+ IBV_PORT_EXTENDED_SPEEDS_SUP = 1 << 14,
+ IBV_PORT_CM_SUP = 1 << 16,
+ IBV_PORT_SNMP_TUNNEL_SUP = 1 << 17,
+ IBV_PORT_REINIT_SUP = 1 << 18,
+ IBV_PORT_DEVICE_MGMT_SUP = 1 << 19,
+ IBV_PORT_VENDOR_CLASS = 1 << 24,
+ IBV_PORT_CLIENT_REG_SUP = 1 << 25,
+ IBV_PORT_IP_BASED_GIDS = 1 << 26,
+};
+
+struct ibv_port_attr {
+ enum ibv_port_state state;
+ enum ibv_mtu max_mtu;
+ enum ibv_mtu active_mtu;
+ int gid_tbl_len;
+ uint32_t port_cap_flags;
+ uint32_t max_msg_sz;
+ uint32_t bad_pkey_cntr;
+ uint32_t qkey_viol_cntr;
+ uint16_t pkey_tbl_len;
+ uint16_t lid;
+ uint16_t sm_lid;
+ uint8_t lmc;
+ uint8_t max_vl_num;
+ uint8_t sm_sl;
+ uint8_t subnet_timeout;
+ uint8_t init_type_reply;
+ uint8_t active_width;
+ uint8_t active_speed;
+ uint8_t phys_state;
+ uint8_t link_layer;
+ uint8_t reserved;
+};
+
+enum ibv_event_type {
+ IBV_EVENT_CQ_ERR,
+ IBV_EVENT_QP_FATAL,
+ IBV_EVENT_QP_REQ_ERR,
+ IBV_EVENT_QP_ACCESS_ERR,
+ IBV_EVENT_COMM_EST,
+ IBV_EVENT_SQ_DRAINED,
+ IBV_EVENT_PATH_MIG,
+ IBV_EVENT_PATH_MIG_ERR,
+ IBV_EVENT_DEVICE_FATAL,
+ IBV_EVENT_PORT_ACTIVE,
+ IBV_EVENT_PORT_ERR,
+ IBV_EVENT_LID_CHANGE,
+ IBV_EVENT_PKEY_CHANGE,
+ IBV_EVENT_SM_CHANGE,
+ IBV_EVENT_SRQ_ERR,
+ IBV_EVENT_SRQ_LIMIT_REACHED,
+ IBV_EVENT_QP_LAST_WQE_REACHED,
+ IBV_EVENT_CLIENT_REREGISTER,
+ IBV_EVENT_GID_CHANGE,
+
+ /* new experimental events start here leaving enough
+ * room for 14 events which should be enough
+ */
+ IBV_EXP_EVENT_DCT_KEY_VIOLATION = 32,
+ IBV_EXP_EVENT_DCT_ACCESS_ERR,
+ IBV_EXP_EVENT_DCT_REQ_ERR,
+};
+
+struct ibv_async_event {
+ union {
+ struct ibv_cq *cq;
+ struct ibv_qp *qp;
+ struct ibv_srq *srq;
+ struct ibv_exp_dct *dct;
+ int port_num;
+ /* For source compatible with Legacy API */
+ uint32_t xrc_qp_num;
+ } element;
+ enum ibv_event_type event_type;
+};
+
+enum ibv_wc_status {
+ IBV_WC_SUCCESS,
+ IBV_WC_LOC_LEN_ERR,
+ IBV_WC_LOC_QP_OP_ERR,
+ IBV_WC_LOC_EEC_OP_ERR,
+ IBV_WC_LOC_PROT_ERR,
+ IBV_WC_WR_FLUSH_ERR,
+ IBV_WC_MW_BIND_ERR,
+ IBV_WC_BAD_RESP_ERR,
+ IBV_WC_LOC_ACCESS_ERR,
+ IBV_WC_REM_INV_REQ_ERR,
+ IBV_WC_REM_ACCESS_ERR,
+ IBV_WC_REM_OP_ERR,
+ IBV_WC_RETRY_EXC_ERR,
+ IBV_WC_RNR_RETRY_EXC_ERR,
+ IBV_WC_LOC_RDD_VIOL_ERR,
+ IBV_WC_REM_INV_RD_REQ_ERR,
+ IBV_WC_REM_ABORT_ERR,
+ IBV_WC_INV_EECN_ERR,
+ IBV_WC_INV_EEC_STATE_ERR,
+ IBV_WC_FATAL_ERR,
+ IBV_WC_RESP_TIMEOUT_ERR,
+ IBV_WC_GENERAL_ERR
+};
+const char *ibv_wc_status_str(enum ibv_wc_status status);
+
+enum ibv_wc_opcode {
+ IBV_WC_SEND,
+ IBV_WC_RDMA_WRITE,
+ IBV_WC_RDMA_READ,
+ IBV_WC_COMP_SWAP,
+ IBV_WC_FETCH_ADD,
+ IBV_WC_BIND_MW,
+/*
+ * Set value of IBV_WC_RECV so consumers can test if a completion is a
+ * receive by testing (opcode & IBV_WC_RECV).
+ */
+ IBV_WC_RECV = 1 << 7,
+ IBV_WC_RECV_RDMA_WITH_IMM
+};
+
+enum ibv_wc_flags {
+ IBV_WC_GRH = 1 << 0,
+ IBV_WC_WITH_IMM = 1 << 1
+};
+
+struct ibv_wc {
+ uint64_t wr_id;
+ enum ibv_wc_status status;
+ enum ibv_wc_opcode opcode;
+ uint32_t vendor_err;
+ uint32_t byte_len;
+ uint32_t imm_data; /* in network byte order */
+ uint32_t qp_num;
+ uint32_t src_qp;
+ int wc_flags;
+ uint16_t pkey_index;
+ uint16_t slid;
+ uint8_t sl;
+ uint8_t dlid_path_bits;
+};
+
+enum ibv_access_flags {
+ IBV_ACCESS_LOCAL_WRITE = 1,
+ IBV_ACCESS_REMOTE_WRITE = (1<<1),
+ IBV_ACCESS_REMOTE_READ = (1<<2),
+ IBV_ACCESS_REMOTE_ATOMIC = (1<<3),
+ IBV_ACCESS_MW_BIND = (1<<4)
+};
+
+struct ibv_pd {
+ struct ibv_context *context;
+ uint32_t handle;
+};
+
+enum ibv_xrcd_init_attr_mask {
+ IBV_XRCD_INIT_ATTR_FD = 1 << 0,
+ IBV_XRCD_INIT_ATTR_OFLAGS = 1 << 1,
+ IBV_XRCD_INIT_ATTR_RESERVED = 1 << 2
+};
+
+struct ibv_xrcd_init_attr {
+ uint32_t comp_mask;
+ int fd;
+ int oflags;
+};
+
+struct ibv_xrcd {
+ struct ibv_context *context;
+};
+
+enum ibv_rereg_mr_flags {
+ IBV_REREG_MR_CHANGE_TRANSLATION = (1 << 0),
+ IBV_REREG_MR_CHANGE_PD = (1 << 1),
+ IBV_REREG_MR_CHANGE_ACCESS = (1 << 2),
+ IBV_REREG_MR_KEEP_VALID = (1 << 3)
+};
+
+struct ibv_mr {
+ struct ibv_context *context;
+ struct ibv_pd *pd;
+ void *addr;
+ size_t length;
+ uint32_t handle;
+ uint32_t lkey;
+ uint32_t rkey;
+};
+
+enum ibv_mw_type {
+ IBV_MW_TYPE_1 = 1,
+ IBV_MW_TYPE_2 = 2
+};
+
+struct ibv_mw {
+ struct ibv_context *context;
+ struct ibv_pd *pd;
+ uint32_t rkey;
+};
+
+struct ibv_global_route {
+ union ibv_gid dgid;
+ uint32_t flow_label;
+ uint8_t sgid_index;
+ uint8_t hop_limit;
+ uint8_t traffic_class;
+};
+
+struct ibv_grh {
+ uint32_t version_tclass_flow;
+ uint16_t paylen;
+ uint8_t next_hdr;
+ uint8_t hop_limit;
+ union ibv_gid sgid;
+ union ibv_gid dgid;
+};
+
+enum ibv_rate {
+ IBV_RATE_MAX = 0,
+ IBV_RATE_2_5_GBPS = 2,
+ IBV_RATE_5_GBPS = 5,
+ IBV_RATE_10_GBPS = 3,
+ IBV_RATE_20_GBPS = 6,
+ IBV_RATE_30_GBPS = 4,
+ IBV_RATE_40_GBPS = 7,
+ IBV_RATE_60_GBPS = 8,
+ IBV_RATE_80_GBPS = 9,
+ IBV_RATE_120_GBPS = 10,
+ IBV_RATE_14_GBPS = 11,
+ IBV_RATE_56_GBPS = 12,
+ IBV_RATE_112_GBPS = 13,
+ IBV_RATE_168_GBPS = 14,
+ IBV_RATE_25_GBPS = 15,
+ IBV_RATE_100_GBPS = 16,
+ IBV_RATE_200_GBPS = 17,
+ IBV_RATE_300_GBPS = 18
+};
+
+/**
+ * ibv_rate_to_mult - Convert the IB rate enum to a multiple of the
+ * base rate of 2.5 Gbit/sec. For example, IBV_RATE_5_GBPS will be
+ * converted to 2, since 5 Gbit/sec is 2 * 2.5 Gbit/sec.
+ * @rate: rate to convert.
+ */
+int ibv_rate_to_mult(enum ibv_rate rate) __attribute_const;
+
+/**
+ * mult_to_ibv_rate - Convert a multiple of 2.5 Gbit/sec to an IB rate enum.
+ * @mult: multiple to convert.
+ */
+enum ibv_rate mult_to_ibv_rate(int mult) __attribute_const;
+
+/**
+ * ibv_rate_to_mbps - Convert the IB rate enum to Mbit/sec.
+ * For example, IBV_RATE_5_GBPS will return the value 5000.
+ * @rate: rate to convert.
+ */
+int ibv_rate_to_mbps(enum ibv_rate rate) __attribute_const;
+
+/**
+ * mbps_to_ibv_rate - Convert a Mbit/sec value to an IB rate enum.
+ * @mbps: value to convert.
+ */
+enum ibv_rate mbps_to_ibv_rate(int mbps) __attribute_const;
+
+struct ibv_ah_attr {
+ struct ibv_global_route grh;
+ uint16_t dlid;
+ uint8_t sl;
+ uint8_t src_path_bits;
+ uint8_t static_rate;
+ uint8_t is_global;
+ uint8_t port_num;
+};
+
+enum ibv_srq_attr_mask {
+ IBV_SRQ_MAX_WR = 1 << 0,
+ IBV_SRQ_LIMIT = 1 << 1
+};
+
+struct ibv_srq_attr {
+ uint32_t max_wr;
+ uint32_t max_sge;
+ uint32_t srq_limit;
+};
+
+struct ibv_srq_init_attr {
+ void *srq_context;
+ struct ibv_srq_attr attr;
+};
+
+enum ibv_srq_type {
+ IBV_SRQT_BASIC,
+ IBV_SRQT_XRC
+};
+
+enum ibv_srq_init_attr_mask {
+ IBV_SRQ_INIT_ATTR_TYPE = 1 << 0,
+ IBV_SRQ_INIT_ATTR_PD = 1 << 1,
+ IBV_SRQ_INIT_ATTR_XRCD = 1 << 2,
+ IBV_SRQ_INIT_ATTR_CQ = 1 << 3,
+ IBV_SRQ_INIT_ATTR_RESERVED = 1 << 4
+};
+
+struct ibv_srq_init_attr_ex {
+ void *srq_context;
+ struct ibv_srq_attr attr;
+
+ uint32_t comp_mask;
+ enum ibv_srq_type srq_type;
+ struct ibv_pd *pd;
+ struct ibv_xrcd *xrcd;
+ struct ibv_cq *cq;
+};
+
+enum ibv_qp_type {
+ IBV_QPT_RC = 2,
+ IBV_QPT_UC,
+ IBV_QPT_UD,
+ /* XRC compatible code */
+ IBV_QPT_XRC,
+ IBV_QPT_RAW_PACKET = 8,
+ IBV_QPT_RAW_ETH = 8,
+ IBV_QPT_XRC_SEND = 9,
+ IBV_QPT_XRC_RECV,
+
+ /* Leave a gap for future qp types before starting with
+ * experimental qp types.
+ */
+ IBV_EXP_QP_TYPE_START = 32,
+ IBV_EXP_QPT_DC_INI = IBV_EXP_QP_TYPE_START
+};
+
+struct ibv_qp_cap {
+ uint32_t max_send_wr;
+ uint32_t max_recv_wr;
+ uint32_t max_send_sge;
+ uint32_t max_recv_sge;
+ uint32_t max_inline_data;
+};
+
+struct ibv_qp_init_attr {
+ void *qp_context;
+ struct ibv_cq *send_cq;
+ struct ibv_cq *recv_cq;
+ struct ibv_srq *srq;
+ struct ibv_qp_cap cap;
+ enum ibv_qp_type qp_type;
+ int sq_sig_all;
+ /* Below is needed for backwards compatabile */
+ struct ibv_xrc_domain *xrc_domain;
+};
+
+enum ibv_qp_init_attr_mask {
+ IBV_QP_INIT_ATTR_PD = 1 << 0,
+ IBV_QP_INIT_ATTR_XRCD = 1 << 1,
+ IBV_QP_INIT_ATTR_RESERVED = 1 << 2
+};
+
+struct ibv_qp_init_attr_ex {
+ void *qp_context;
+ struct ibv_cq *send_cq;
+ struct ibv_cq *recv_cq;
+ struct ibv_srq *srq;
+ struct ibv_qp_cap cap;
+ enum ibv_qp_type qp_type;
+ int sq_sig_all;
+
+ uint32_t comp_mask;
+ struct ibv_pd *pd;
+ struct ibv_xrcd *xrcd;
+};
+
+enum ibv_qp_open_attr_mask {
+ IBV_QP_OPEN_ATTR_NUM = 1 << 0,
+ IBV_QP_OPEN_ATTR_XRCD = 1 << 1,
+ IBV_QP_OPEN_ATTR_CONTEXT = 1 << 2,
+ IBV_QP_OPEN_ATTR_TYPE = 1 << 3,
+ IBV_QP_OPEN_ATTR_RESERVED = 1 << 4
+};
+
+struct ibv_qp_open_attr {
+ uint32_t comp_mask;
+ uint32_t qp_num;
+ struct ibv_xrcd *xrcd;
+ void *qp_context;
+ enum ibv_qp_type qp_type;
+};
+
+enum ibv_qp_attr_mask {
+ IBV_QP_STATE = 1 << 0,
+ IBV_QP_CUR_STATE = 1 << 1,
+ IBV_QP_EN_SQD_ASYNC_NOTIFY = 1 << 2,
+ IBV_QP_ACCESS_FLAGS = 1 << 3,
+ IBV_QP_PKEY_INDEX = 1 << 4,
+ IBV_QP_PORT = 1 << 5,
+ IBV_QP_QKEY = 1 << 6,
+ IBV_QP_AV = 1 << 7,
+ IBV_QP_PATH_MTU = 1 << 8,
+ IBV_QP_TIMEOUT = 1 << 9,
+ IBV_QP_RETRY_CNT = 1 << 10,
+ IBV_QP_RNR_RETRY = 1 << 11,
+ IBV_QP_RQ_PSN = 1 << 12,
+ IBV_QP_MAX_QP_RD_ATOMIC = 1 << 13,
+ IBV_QP_ALT_PATH = 1 << 14,
+ IBV_QP_MIN_RNR_TIMER = 1 << 15,
+ IBV_QP_SQ_PSN = 1 << 16,
+ IBV_QP_MAX_DEST_RD_ATOMIC = 1 << 17,
+ IBV_QP_PATH_MIG_STATE = 1 << 18,
+ IBV_QP_CAP = 1 << 19,
+ IBV_QP_DEST_QPN = 1 << 20
+};
+
+enum ibv_qp_state {
+ IBV_QPS_RESET,
+ IBV_QPS_INIT,
+ IBV_QPS_RTR,
+ IBV_QPS_RTS,
+ IBV_QPS_SQD,
+ IBV_QPS_SQE,
+ IBV_QPS_ERR,
+ IBV_QPS_UNKNOWN
+};
+
+enum ibv_mig_state {
+ IBV_MIG_MIGRATED,
+ IBV_MIG_REARM,
+ IBV_MIG_ARMED
+};
+
+struct ibv_qp_attr {
+ enum ibv_qp_state qp_state;
+ enum ibv_qp_state cur_qp_state;
+ enum ibv_mtu path_mtu;
+ enum ibv_mig_state path_mig_state;
+ uint32_t qkey;
+ uint32_t rq_psn;
+ uint32_t sq_psn;
+ uint32_t dest_qp_num;
+ int qp_access_flags;
+ struct ibv_qp_cap cap;
+ struct ibv_ah_attr ah_attr;
+ struct ibv_ah_attr alt_ah_attr;
+ uint16_t pkey_index;
+ uint16_t alt_pkey_index;
+ uint8_t en_sqd_async_notify;
+ uint8_t sq_draining;
+ uint8_t max_rd_atomic;
+ uint8_t max_dest_rd_atomic;
+ uint8_t min_rnr_timer;
+ uint8_t port_num;
+ uint8_t timeout;
+ uint8_t retry_cnt;
+ uint8_t rnr_retry;
+ uint8_t alt_port_num;
+ uint8_t alt_timeout;
+};
+
+enum ibv_wr_opcode {
+ IBV_WR_RDMA_WRITE,
+ IBV_WR_RDMA_WRITE_WITH_IMM,
+ IBV_WR_SEND,
+ IBV_WR_SEND_WITH_IMM,
+ IBV_WR_RDMA_READ,
+ IBV_WR_ATOMIC_CMP_AND_SWP,
+ IBV_WR_ATOMIC_FETCH_AND_ADD
+};
+
+enum ibv_send_flags {
+ IBV_SEND_FENCE = 1 << 0,
+ IBV_SEND_SIGNALED = 1 << 1,
+ IBV_SEND_SOLICITED = 1 << 2,
+ IBV_SEND_INLINE = 1 << 3
+};
+
+struct ibv_sge {
+ uint64_t addr;
+ uint32_t length;
+ uint32_t lkey;
+};
+
+struct ibv_send_wr {
+ uint64_t wr_id;
+ struct ibv_send_wr *next;
+ struct ibv_sge *sg_list;
+ int num_sge;
+ enum ibv_wr_opcode opcode;
+ int send_flags;
+ uint32_t imm_data; /* in network byte order */
+ union {
+ struct {
+ uint64_t remote_addr;
+ uint32_t rkey;
+ } rdma;
+ struct {
+ uint64_t remote_addr;
+ uint64_t compare_add;
+ uint64_t swap;
+ uint32_t rkey;
+ } atomic;
+ struct {
+ struct ibv_ah *ah;
+ uint32_t remote_qpn;
+ uint32_t remote_qkey;
+ } ud;
+ } wr;
+ union {
+ union {
+ struct {
+ uint32_t remote_srqn;
+ } xrc;
+ } qp_type;
+
+ uint32_t xrc_remote_srq_num;
+ };
+};
+
+struct ibv_recv_wr {
+ uint64_t wr_id;
+ struct ibv_recv_wr *next;
+ struct ibv_sge *sg_list;
+ int num_sge;
+};
+
+struct ibv_mw_bind {
+ uint64_t wr_id;
+ struct ibv_mr *mr;
+ void *addr;
+ size_t length;
+ int send_flags;
+ int mw_access_flags;
+};
+
+struct ibv_srq {
+ struct ibv_context *context;
+ void *srq_context;
+ struct ibv_pd *pd;
+ uint32_t handle;
+
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ uint32_t events_completed;
+
+ /* below are for source compatabilty with legacy XRC,
+ * padding based on ibv_srq_legacy.
+ */
+ uint32_t xrc_srq_num_bin_compat_padding;
+ struct ibv_xrc_domain *xrc_domain_bin_compat_padding;
+ struct ibv_cq *xrc_cq_bin_compat_padding;
+ void *ibv_srq_padding;
+
+ /* legacy fields */
+ uint32_t xrc_srq_num;
+ struct ibv_xrc_domain *xrc_domain;
+ struct ibv_cq *xrc_cq;
+};
+
+/* Not in use in new API, needed for compilation as part of source compat layer */
+enum ibv_event_flags {
+ IBV_XRC_QP_EVENT_FLAG = 0x80000000,
+};
+
+
+
+struct ibv_qp {
+ struct ibv_context *context;
+ void *qp_context;
+ struct ibv_pd *pd;
+ struct ibv_cq *send_cq;
+ struct ibv_cq *recv_cq;
+ struct ibv_srq *srq;
+ uint32_t handle;
+ uint32_t qp_num;
+ enum ibv_qp_state state;
+ enum ibv_qp_type qp_type;
+
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ uint32_t events_completed;
+};
+
+struct ibv_comp_channel {
+ struct ibv_context *context;
+ int fd;
+ int refcnt;
+};
+
+struct ibv_cq {
+ struct ibv_context *context;
+ struct ibv_comp_channel *channel;
+ void *cq_context;
+ uint32_t handle;
+ int cqe;
+
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ uint32_t comp_events_completed;
+ uint32_t async_events_completed;
+};
+
+struct ibv_ah {
+ struct ibv_context *context;
+ struct ibv_pd *pd;
+ uint32_t handle;
+};
+
+enum ibv_flow_flags {
+ IBV_FLOW_ATTR_FLAGS_ALLOW_LOOP_BACK = 1,
+ IBV_FLOW_ATTR_FLAGS_DONT_TRAP = 1 << 1,
+};
+
+enum ibv_flow_attr_type {
+ /* steering according to rule specifications */
+ IBV_FLOW_ATTR_NORMAL = 0x0,
+ /* default unicast and multicast rule -
+ * receive all Eth traffic which isn't steered to any QP
+ */
+ IBV_FLOW_ATTR_ALL_DEFAULT = 0x1,
+ /* default multicast rule -
+ * receive all Eth multicast traffic which isn't steered to any QP
+ */
+ IBV_FLOW_ATTR_MC_DEFAULT = 0x2,
+};
+
+enum ibv_flow_spec_type {
+ IBV_FLOW_SPEC_ETH = 0x20,
+ IBV_FLOW_SPEC_IPV4 = 0x30,
+ IBV_FLOW_SPEC_TCP = 0x40,
+ IBV_FLOW_SPEC_UDP = 0x41,
+};
+
+struct ibv_flow_eth_filter {
+ uint8_t dst_mac[6];
+ uint8_t src_mac[6];
+ uint16_t ether_type;
+ /*
+ * same layout as 802.1q: prio 3, cfi 1, vlan id 12
+ */
+ uint16_t vlan_tag;
+};
+
+struct ibv_flow_spec_eth {
+ enum ibv_flow_spec_type type;
+ uint16_t size;
+ struct ibv_flow_eth_filter val;
+ struct ibv_flow_eth_filter mask;
+};
+
+struct ibv_flow_ipv4_filter {
+ uint32_t src_ip;
+ uint32_t dst_ip;
+};
+
+struct ibv_flow_spec_ipv4 {
+ enum ibv_flow_spec_type type;
+ uint16_t size;
+ struct ibv_flow_ipv4_filter val;
+ struct ibv_flow_ipv4_filter mask;
+};
+
+struct ibv_flow_tcp_udp_filter {
+ uint16_t dst_port;
+ uint16_t src_port;
+};
+
+struct ibv_flow_spec_tcp_udp {
+ enum ibv_flow_spec_type type;
+ uint16_t size;
+ struct ibv_flow_tcp_udp_filter val;
+ struct ibv_flow_tcp_udp_filter mask;
+};
+
+struct ibv_flow_spec {
+ union {
+ struct {
+ enum ibv_flow_spec_type type;
+ uint16_t size;
+ } hdr;
+ struct ibv_flow_spec_eth eth;
+ struct ibv_flow_spec_ipv4 ipv4;
+ struct ibv_flow_spec_tcp_udp tcp_udp;
+ };
+};
+
+struct ibv_flow_attr {
+ uint32_t comp_mask;
+ enum ibv_flow_attr_type type;
+ uint16_t size;
+ uint16_t priority;
+ uint8_t num_of_specs;
+ uint8_t port;
+ uint32_t flags;
+ /* Following are the optional layers according to user request
+ * struct ibv_flow_spec_xxx [L2]
+ * struct ibv_flow_spec_yyy [L3/L4]
+ */
+};
+
+struct ibv_flow {
+ uint32_t comp_mask;
+ struct ibv_context *context;
+ uint32_t handle;
+};
+
+struct ibv_device;
+struct ibv_context;
+
+struct ibv_device_ops {
+ struct ibv_context * (*alloc_context)(struct ibv_device *device, int cmd_fd);
+ void (*free_context)(struct ibv_context *context);
+};
+
+enum {
+ IBV_SYSFS_NAME_MAX = 64,
+ IBV_SYSFS_PATH_MAX = 256
+};
+
+struct ibv_device {
+ struct ibv_device_ops ops;
+ enum ibv_node_type node_type;
+ enum ibv_transport_type transport_type;
+ /* Name of underlying kernel IB device, eg "mthca0" */
+ char name[IBV_SYSFS_NAME_MAX];
+ /* Name of uverbs device, eg "uverbs0" */
+ char dev_name[IBV_SYSFS_NAME_MAX];
+ /* Path to infiniband_verbs class device in sysfs */
+ char dev_path[IBV_SYSFS_PATH_MAX];
+ /* Path to infiniband class device in sysfs */
+ char ibdev_path[IBV_SYSFS_PATH_MAX];
+};
+
+struct verbs_device {
+ struct ibv_device device; /* Must be first */
+ size_t sz;
+ size_t size_of_context;
+ int (*init_context)(struct verbs_device *device,
+ struct ibv_context *ctx, int cmd_fd);
+ void (*uninit_context)(struct verbs_device *device,
+ struct ibv_context *ctx);
+ /* future fields added here */
+};
+
+struct ibv_context_ops {
+ int (*query_device)(struct ibv_context *context,
+ struct ibv_device_attr *device_attr);
+ int (*query_port)(struct ibv_context *context, uint8_t port_num,
+ struct ibv_port_attr *port_attr);
+ struct ibv_pd * (*alloc_pd)(struct ibv_context *context);
+ int (*dealloc_pd)(struct ibv_pd *pd);
+ struct ibv_mr * (*reg_mr)(struct ibv_pd *pd, void *addr, size_t length,
+ int access);
+ struct ibv_mr * (*rereg_mr)(struct ibv_mr *mr,
+ int flags,
+ struct ibv_pd *pd, void *addr,
+ size_t length,
+ int access);
+ int (*dereg_mr)(struct ibv_mr *mr);
+ struct ibv_mw * (*alloc_mw)(struct ibv_pd *pd, enum ibv_mw_type type);
+ int (*bind_mw)(struct ibv_qp *qp, struct ibv_mw *mw,
+ struct ibv_mw_bind *mw_bind);
+ int (*dealloc_mw)(struct ibv_mw *mw);
+ struct ibv_cq * (*create_cq)(struct ibv_context *context, int cqe,
+ struct ibv_comp_channel *channel,
+ int comp_vector);
+ int (*poll_cq)(struct ibv_cq *cq, int num_entries, struct ibv_wc *wc);
+ int (*req_notify_cq)(struct ibv_cq *cq, int solicited_only);
+ void (*cq_event)(struct ibv_cq *cq);
+ int (*resize_cq)(struct ibv_cq *cq, int cqe);
+ int (*destroy_cq)(struct ibv_cq *cq);
+ struct ibv_srq * (*create_srq)(struct ibv_pd *pd,
+ struct ibv_srq_init_attr *srq_init_attr);
+ int (*modify_srq)(struct ibv_srq *srq,
+ struct ibv_srq_attr *srq_attr,
+ int srq_attr_mask);
+ int (*query_srq)(struct ibv_srq *srq,
+ struct ibv_srq_attr *srq_attr);
+ int (*destroy_srq)(struct ibv_srq *srq);
+ int (*post_srq_recv)(struct ibv_srq *srq,
+ struct ibv_recv_wr *recv_wr,
+ struct ibv_recv_wr **bad_recv_wr);
+ struct ibv_qp * (*create_qp)(struct ibv_pd *pd, struct ibv_qp_init_attr *attr);
+ int (*query_qp)(struct ibv_qp *qp, struct ibv_qp_attr *attr,
+ int attr_mask,
+ struct ibv_qp_init_attr *init_attr);
+ int (*modify_qp)(struct ibv_qp *qp, struct ibv_qp_attr *attr,
+ int attr_mask);
+ int (*destroy_qp)(struct ibv_qp *qp);
+ int (*post_send)(struct ibv_qp *qp, struct ibv_send_wr *wr,
+ struct ibv_send_wr **bad_wr);
+ int (*post_recv)(struct ibv_qp *qp, struct ibv_recv_wr *wr,
+ struct ibv_recv_wr **bad_wr);
+ struct ibv_ah * (*create_ah)(struct ibv_pd *pd, struct ibv_ah_attr *attr);
+ int (*destroy_ah)(struct ibv_ah *ah);
+ int (*attach_mcast)(struct ibv_qp *qp, const union ibv_gid *gid,
+ uint16_t lid);
+ int (*detach_mcast)(struct ibv_qp *qp, const union ibv_gid *gid,
+ uint16_t lid);
+ void (*async_event)(struct ibv_async_event *event);
+};
+
+struct ibv_context {
+ struct ibv_device *device;
+ struct ibv_context_ops ops;
+ int cmd_fd;
+ int async_fd;
+ int num_comp_vectors;
+ pthread_mutex_t mutex;
+ void *abi_compat;
+};
+
+enum verbs_context_mask {
+ VERBS_CONTEXT_XRCD = (uint64_t)1 << 0,
+ VERBS_CONTEXT_SRQ = (uint64_t)1 << 1,
+ VERBS_CONTEXT_QP = (uint64_t)1 << 2,
+ VERBS_CONTEXT_RESERVED = (uint64_t)1 << 3,
+ VERBS_CONTEXT_EXP = (uint64_t)1 << 62
+};
+
+struct verbs_context {
+ /* "grows up" - new fields go here */
+ int (*_reserved_2) (void);
+ int (*destroy_flow) (struct ibv_flow *flow);
+ int (*_reserved_1) (void);
+ struct ibv_flow * (*create_flow) (struct ibv_qp *qp,
+ struct ibv_flow_attr *flow_attr);
+ struct ibv_qp * (*open_qp)(struct ibv_context *context,
+ struct ibv_qp_open_attr *attr);
+ struct ibv_qp * (*create_qp_ex)(struct ibv_context *context,
+ struct ibv_qp_init_attr_ex *qp_init_attr_ex);
+ int (*get_srq_num)(struct ibv_srq *srq, uint32_t *srq_num);
+ struct ibv_srq * (*create_srq_ex)(struct ibv_context *context,
+ struct ibv_srq_init_attr_ex *srq_init_attr_ex);
+ struct ibv_xrcd * (*open_xrcd)(struct ibv_context *context,
+ struct ibv_xrcd_init_attr *xrcd_init_attr);
+ int (*close_xrcd)(struct ibv_xrcd *xrcd);
+ uint64_t has_comp_mask;
+ size_t sz; /* Must be immediately before struct ibv_context */
+ struct ibv_context context;/* Must be last field in the struct */
+};
+
+static inline struct verbs_context *verbs_get_ctx(struct ibv_context *ctx)
+{
+ return (!ctx || (ctx->abi_compat != __VERBS_ABI_IS_EXTENDED)) ?
+ NULL : container_of(ctx, struct verbs_context, context);
+}
+
+#define verbs_get_ctx_op(ctx, op) ({ \
+ struct verbs_context *_vctx = verbs_get_ctx(ctx); \
+ (!_vctx || (_vctx->sz < sizeof(*_vctx) - offsetof(struct verbs_context, op)) || \
+ !_vctx->op) ? NULL : _vctx; })
+
+#define verbs_set_ctx_op(_vctx, op, ptr) ({ \
+ struct verbs_context *vctx = _vctx; \
+ if (vctx && (vctx->sz >= sizeof(*vctx) - offsetof(struct verbs_context, op))) \
+ vctx->op = ptr; })
+
+static inline struct verbs_device *verbs_get_device(struct ibv_device *dev)
+{
+ return (dev->ops.alloc_context) ?
+ NULL : container_of(dev, struct verbs_device, device);
+}
+
+/**
+ * ibv_get_device_list - Get list of IB devices currently available
+ * @num_devices: optional. if non-NULL, set to the number of devices
+ * returned in the array.
+ *
+ * Return a NULL-terminated array of IB devices. The array can be
+ * released with ibv_free_device_list().
+ */
+struct ibv_device **ibv_get_device_list(int *num_devices);
+
+/**
+ * ibv_free_device_list - Free list from ibv_get_device_list()
+ *
+ * Free an array of devices returned from ibv_get_device_list(). Once
+ * the array is freed, pointers to devices that were not opened with
+ * ibv_open_device() are no longer valid. Client code must open all
+ * devices it intends to use before calling ibv_free_device_list().
+ */
+void ibv_free_device_list(struct ibv_device **list);
+
+/**
+ * ibv_get_device_name - Return kernel device name
+ */
+const char *ibv_get_device_name(struct ibv_device *device);
+
+/**
+ * ibv_get_device_guid - Return device's node GUID
+ */
+uint64_t ibv_get_device_guid(struct ibv_device *device);
+
+/**
+ * ibv_open_device - Initialize device for use
+ */
+struct ibv_context *ibv_open_device(struct ibv_device *device);
+
+/**
+ * ibv_close_device - Release device
+ */
+int ibv_close_device(struct ibv_context *context);
+
+/**
+ * ibv_get_async_event - Get next async event
+ * @event: Pointer to use to return async event
+ *
+ * All async events returned by ibv_get_async_event() must eventually
+ * be acknowledged with ibv_ack_async_event().
+ */
+int ibv_get_async_event(struct ibv_context *context,
+ struct ibv_async_event *event);
+
+/**
+ * ibv_ack_async_event - Acknowledge an async event
+ * @event: Event to be acknowledged.
+ *
+ * All async events which are returned by ibv_get_async_event() must
+ * be acknowledged. To avoid races, destroying an object (CQ, SRQ or
+ * QP) will wait for all affiliated events to be acknowledged, so
+ * there should be a one-to-one correspondence between acks and
+ * successful gets.
+ */
+void ibv_ack_async_event(struct ibv_async_event *event);
+
+/**
+ * ibv_query_device - Get device properties
+ */
+int ibv_query_device(struct ibv_context *context,
+ struct ibv_device_attr *device_attr);
+
+/**
+ * ibv_query_port - Get port properties
+ */
+int ibv_query_port(struct ibv_context *context, uint8_t port_num,
+ struct ibv_port_attr *port_attr);
+
+static inline int ___ibv_query_port(struct ibv_context *context,
+ uint8_t port_num,
+ struct ibv_port_attr *port_attr)
+{
+ /* For compatibility when running with old libibverbs */
+ port_attr->link_layer = IBV_LINK_LAYER_UNSPECIFIED;
+ port_attr->reserved = 0;
+
+ return ibv_query_port(context, port_num, port_attr);
+}
+
+#define ibv_query_port(context, port_num, port_attr) \
+ ___ibv_query_port(context, port_num, port_attr)
+
+/**
+ * ibv_query_gid - Get a GID table entry
+ */
+int ibv_query_gid(struct ibv_context *context, uint8_t port_num,
+ int index, union ibv_gid *gid);
+
+/**
+ * ibv_query_pkey - Get a P_Key table entry
+ */
+int ibv_query_pkey(struct ibv_context *context, uint8_t port_num,
+ int index, uint16_t *pkey);
+
+/**
+ * ibv_alloc_pd - Allocate a protection domain
+ */
+struct ibv_pd *ibv_alloc_pd(struct ibv_context *context);
+
+/**
+ * ibv_dealloc_pd - Free a protection domain
+ */
+int ibv_dealloc_pd(struct ibv_pd *pd);
+
+static inline struct ibv_flow *ibv_create_flow(struct ibv_qp *qp,
+ struct ibv_flow_attr *flow)
+{
+ struct verbs_context *vctx = verbs_get_ctx_op(qp->context,
+ create_flow);
+ if (!vctx)
+ return NULL;
+
+ return vctx->create_flow(qp, flow);
+}
+
+static inline int ibv_destroy_flow(struct ibv_flow *flow_id)
+{
+ struct verbs_context *vctx = verbs_get_ctx_op(flow_id->context,
+ destroy_flow);
+ if (!vctx)
+ return -ENOSYS;
+ return vctx->destroy_flow(flow_id);
+}
+
+/**
+ * ibv_open_xrcd - Open an extended connection domain
+ */
+static inline struct ibv_xrcd *
+ibv_open_xrcd(struct ibv_context *context, struct ibv_xrcd_init_attr *xrcd_init_attr)
+{
+ struct verbs_context *vctx = verbs_get_ctx_op(context, open_xrcd);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+ return vctx->open_xrcd(context, xrcd_init_attr);
+}
+
+/**
+ * ibv_close_xrcd - Close an extended connection domain
+ */
+static inline int ibv_close_xrcd(struct ibv_xrcd *xrcd)
+{
+ struct verbs_context *vctx = verbs_get_ctx(xrcd->context);
+ return vctx->close_xrcd(xrcd);
+}
+
+/**
+ * ibv_reg_mr - Register a memory region
+ */
+struct ibv_mr *ibv_reg_mr(struct ibv_pd *pd, void *addr,
+ size_t length, int access);
+
+/**
+ * ibv_dereg_mr - Deregister a memory region
+ */
+int ibv_dereg_mr(struct ibv_mr *mr);
+
+/**
+ * ibv_alloc_mw - Allocate a memory window
+ */
+static inline struct ibv_mw *ibv_alloc_mw(struct ibv_pd *pd,
+ enum ibv_mw_type type)
+{
+ if (!pd->context->ops.alloc_mw) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ struct ibv_mw *mw = pd->context->ops.alloc_mw(pd, type);
+ if (mw) {
+ mw->context = pd->context;
+ mw->pd = pd;
+ }
+ return mw;
+}
+
+/**
+ * ibv_dealloc_mw - Free a memory window
+ */
+static inline int ibv_dealloc_mw(struct ibv_mw *mw)
+{
+ return mw->context->ops.dealloc_mw(mw);
+}
+
+/**
+ * ibv_inc_rkey - increase the 8 lsb in the given rkey
+ */
+static inline uint32_t ibv_inc_rkey(uint32_t rkey)
+{
+ const uint32_t mask = 0x000000ff;
+ uint8_t newtag = (uint8_t) ((rkey + 1) & mask);
+ return (rkey & ~mask) | newtag;
+}
+
+/**
+ * ibv_create_comp_channel - Create a completion event channel
+ */
+struct ibv_comp_channel *ibv_create_comp_channel(struct ibv_context *context);
+
+/**
+ * ibv_destroy_comp_channel - Destroy a completion event channel
+ */
+int ibv_destroy_comp_channel(struct ibv_comp_channel *channel);
+
+/**
+ * ibv_create_cq - Create a completion queue
+ * @context - Context CQ will be attached to
+ * @cqe - Minimum number of entries required for CQ
+ * @cq_context - Consumer-supplied context returned for completion events
+ * @channel - Completion channel where completion events will be queued.
+ * May be NULL if completion events will not be used.
+ * @comp_vector - Completion vector used to signal completion events.
+ * Must be >= 0 and < context->num_comp_vectors.
+ */
+struct ibv_cq *ibv_create_cq(struct ibv_context *context, int cqe,
+ void *cq_context,
+ struct ibv_comp_channel *channel,
+ int comp_vector);
+
+/**
+ * ibv_resize_cq - Modifies the capacity of the CQ.
+ * @cq: The CQ to resize.
+ * @cqe: The minimum size of the CQ.
+ *
+ * Users can examine the cq structure to determine the actual CQ size.
+ */
+int ibv_resize_cq(struct ibv_cq *cq, int cqe);
+
+/**
+ * ibv_destroy_cq - Destroy a completion queue
+ */
+int ibv_destroy_cq(struct ibv_cq *cq);
+
+/**
+ * ibv_get_cq_event - Read next CQ event
+ * @channel: Channel to get next event from.
+ * @cq: Used to return pointer to CQ.
+ * @cq_context: Used to return consumer-supplied CQ context.
+ *
+ * All completion events returned by ibv_get_cq_event() must
+ * eventually be acknowledged with ibv_ack_cq_events().
+ */
+int ibv_get_cq_event(struct ibv_comp_channel *channel,
+ struct ibv_cq **cq, void **cq_context);
+
+/**
+ * ibv_ack_cq_events - Acknowledge CQ completion events
+ * @cq: CQ to acknowledge events for
+ * @nevents: Number of events to acknowledge.
+ *
+ * All completion events which are returned by ibv_get_cq_event() must
+ * be acknowledged. To avoid races, ibv_destroy_cq() will wait for
+ * all completion events to be acknowledged, so there should be a
+ * one-to-one correspondence between acks and successful gets. An
+ * application may accumulate multiple completion events and
+ * acknowledge them in a single call to ibv_ack_cq_events() by passing
+ * the number of events to ack in @nevents.
+ */
+void ibv_ack_cq_events(struct ibv_cq *cq, unsigned int nevents);
+
+/**
+ * ibv_poll_cq - Poll a CQ for work completions
+ * @cq:the CQ being polled
+ * @num_entries:maximum number of completions to return
+ * @wc:array of at least @num_entries of &struct ibv_wc where completions
+ * will be returned
+ *
+ * Poll a CQ for (possibly multiple) completions. If the return value
+ * is < 0, an error occurred. If the return value is >= 0, it is the
+ * number of completions returned. If the return value is
+ * non-negative and strictly less than num_entries, then the CQ was
+ * emptied.
+ */
+static inline int ibv_poll_cq(struct ibv_cq *cq, int num_entries, struct ibv_wc *wc)
+{
+ return cq->context->ops.poll_cq(cq, num_entries, wc);
+}
+
+/**
+ * ibv_req_notify_cq - Request completion notification on a CQ. An
+ * event will be added to the completion channel associated with the
+ * CQ when an entry is added to the CQ.
+ * @cq: The completion queue to request notification for.
+ * @solicited_only: If non-zero, an event will be generated only for
+ * the next solicited CQ entry. If zero, any CQ entry, solicited or
+ * not, will generate an event.
+ */
+static inline int ibv_req_notify_cq(struct ibv_cq *cq, int solicited_only)
+{
+ return cq->context->ops.req_notify_cq(cq, solicited_only);
+}
+
+/**
+ * ibv_create_srq - Creates a SRQ associated with the specified protection
+ * domain.
+ * @pd: The protection domain associated with the SRQ.
+ * @srq_init_attr: A list of initial attributes required to create the SRQ.
+ *
+ * srq_attr->max_wr and srq_attr->max_sge are read the determine the
+ * requested size of the SRQ, and set to the actual values allocated
+ * on return. If ibv_create_srq() succeeds, then max_wr and max_sge
+ * will always be at least as large as the requested values.
+ */
+struct ibv_srq *ibv_create_srq(struct ibv_pd *pd,
+ struct ibv_srq_init_attr *srq_init_attr);
+
+static inline struct ibv_srq *
+ibv_create_srq_ex(struct ibv_context *context,
+ struct ibv_srq_init_attr_ex *srq_init_attr_ex)
+{
+ struct verbs_context *vctx;
+ uint32_t mask = srq_init_attr_ex->comp_mask;
+
+ if (!(mask & ~(IBV_SRQ_INIT_ATTR_PD | IBV_SRQ_INIT_ATTR_TYPE)) &&
+ (mask & IBV_SRQ_INIT_ATTR_PD) &&
+ (!(mask & IBV_SRQ_INIT_ATTR_TYPE) ||
+ (srq_init_attr_ex->srq_type == IBV_SRQT_BASIC)))
+ return ibv_create_srq(srq_init_attr_ex->pd,
+ (struct ibv_srq_init_attr *) srq_init_attr_ex);
+
+ vctx = verbs_get_ctx_op(context, create_srq_ex);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+ return vctx->create_srq_ex(context, srq_init_attr_ex);
+}
+
+/**
+ * ibv_modify_srq - Modifies the attributes for the specified SRQ.
+ * @srq: The SRQ to modify.
+ * @srq_attr: On input, specifies the SRQ attributes to modify. On output,
+ * the current values of selected SRQ attributes are returned.
+ * @srq_attr_mask: A bit-mask used to specify which attributes of the SRQ
+ * are being modified.
+ *
+ * The mask may contain IBV_SRQ_MAX_WR to resize the SRQ and/or
+ * IBV_SRQ_LIMIT to set the SRQ's limit and request notification when
+ * the number of receives queued drops below the limit.
+ */
+int ibv_modify_srq(struct ibv_srq *srq,
+ struct ibv_srq_attr *srq_attr,
+ int srq_attr_mask);
+
+/**
+ * ibv_query_srq - Returns the attribute list and current values for the
+ * specified SRQ.
+ * @srq: The SRQ to query.
+ * @srq_attr: The attributes of the specified SRQ.
+ */
+int ibv_query_srq(struct ibv_srq *srq, struct ibv_srq_attr *srq_attr);
+
+static inline int ibv_get_srq_num(struct ibv_srq *srq, uint32_t *srq_num)
+{
+ struct verbs_context *vctx = verbs_get_ctx_op(srq->context, get_srq_num);
+
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->get_srq_num(srq, srq_num);
+}
+
+/**
+ * ibv_destroy_srq - Destroys the specified SRQ.
+ * @srq: The SRQ to destroy.
+ */
+int ibv_destroy_srq(struct ibv_srq *srq);
+
+/**
+ * ibv_post_srq_recv - Posts a list of work requests to the specified SRQ.
+ * @srq: The SRQ to post the work request on.
+ * @recv_wr: A list of work requests to post on the receive queue.
+ * @bad_recv_wr: On an immediate failure, this parameter will reference
+ * the work request that failed to be posted on the QP.
+ */
+static inline int ibv_post_srq_recv(struct ibv_srq *srq,
+ struct ibv_recv_wr *recv_wr,
+ struct ibv_recv_wr **bad_recv_wr)
+{
+ return srq->context->ops.post_srq_recv(srq, recv_wr, bad_recv_wr);
+}
+
+/**
+ * ibv_create_qp - Create a queue pair.
+ */
+struct ibv_qp *ibv_create_qp(struct ibv_pd *pd,
+ struct ibv_qp_init_attr *qp_init_attr);
+
+static inline struct ibv_qp *
+ibv_create_qp_ex(struct ibv_context *context, struct ibv_qp_init_attr_ex *qp_init_attr_ex)
+{
+ struct verbs_context *vctx;
+ uint32_t mask = qp_init_attr_ex->comp_mask;
+
+ if (mask == IBV_QP_INIT_ATTR_PD)
+ return ibv_create_qp(qp_init_attr_ex->pd,
+ (struct ibv_qp_init_attr *) qp_init_attr_ex);
+
+ vctx = verbs_get_ctx_op(context, create_qp_ex);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+ return vctx->create_qp_ex(context, qp_init_attr_ex);
+}
+
+/**
+ * ibv_open_qp - Open a shareable queue pair.
+ */
+static inline struct ibv_qp *
+ibv_open_qp(struct ibv_context *context, struct ibv_qp_open_attr *qp_open_attr)
+{
+ struct verbs_context *vctx = verbs_get_ctx_op(context, open_qp);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+ return vctx->open_qp(context, qp_open_attr);
+}
+
+/**
+ * ibv_modify_qp - Modify a queue pair.
+ */
+int ibv_modify_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr,
+ int attr_mask);
+
+/**
+ * ibv_query_qp - Returns the attribute list and current values for the
+ * specified QP.
+ * @qp: The QP to query.
+ * @attr: The attributes of the specified QP.
+ * @attr_mask: A bit-mask used to select specific attributes to query.
+ * @init_attr: Additional attributes of the selected QP.
+ *
+ * The qp_attr_mask may be used to limit the query to gathering only the
+ * selected attributes.
+ */
+int ibv_query_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr,
+ int attr_mask,
+ struct ibv_qp_init_attr *init_attr);
+
+/**
+ * ibv_destroy_qp - Destroy a queue pair.
+ */
+int ibv_destroy_qp(struct ibv_qp *qp);
+
+/**
+ * ibv_post_send - Post a list of work requests to a send queue.
+ *
+ * If IBV_SEND_INLINE flag is set, the data buffers can be reused
+ * immediately after the call returns.
+ */
+static inline int ibv_post_send(struct ibv_qp *qp, struct ibv_send_wr *wr,
+ struct ibv_send_wr **bad_wr)
+{
+ return qp->context->ops.post_send(qp, wr, bad_wr);
+}
+
+/**
+ * ibv_post_recv - Post a list of work requests to a receive queue.
+ */
+static inline int ibv_post_recv(struct ibv_qp *qp, struct ibv_recv_wr *wr,
+ struct ibv_recv_wr **bad_wr)
+{
+ return qp->context->ops.post_recv(qp, wr, bad_wr);
+}
+
+/**
+ * ibv_create_ah - Create an address handle.
+ */
+struct ibv_ah *ibv_create_ah(struct ibv_pd *pd, struct ibv_ah_attr *attr);
+
+/**
+ * ibv_init_ah_from_wc - Initializes address handle attributes from a
+ * work completion.
+ * @context: Device context on which the received message arrived.
+ * @port_num: Port on which the received message arrived.
+ * @wc: Work completion associated with the received message.
+ * @grh: References the received global route header. This parameter is
+ * ignored unless the work completion indicates that the GRH is valid.
+ * @ah_attr: Returned attributes that can be used when creating an address
+ * handle for replying to the message.
+ */
+int ibv_init_ah_from_wc(struct ibv_context *context, uint8_t port_num,
+ struct ibv_wc *wc, struct ibv_grh *grh,
+ struct ibv_ah_attr *ah_attr);
+
+/**
+ * ibv_create_ah_from_wc - Creates an address handle associated with the
+ * sender of the specified work completion.
+ * @pd: The protection domain associated with the address handle.
+ * @wc: Work completion information associated with a received message.
+ * @grh: References the received global route header. This parameter is
+ * ignored unless the work completion indicates that the GRH is valid.
+ * @port_num: The outbound port number to associate with the address.
+ *
+ * The address handle is used to reference a local or global destination
+ * in all UD QP post sends.
+ */
+struct ibv_ah *ibv_create_ah_from_wc(struct ibv_pd *pd, struct ibv_wc *wc,
+ struct ibv_grh *grh, uint8_t port_num);
+
+/**
+ * ibv_destroy_ah - Destroy an address handle.
+ */
+int ibv_destroy_ah(struct ibv_ah *ah);
+
+/**
+ * ibv_attach_mcast - Attaches the specified QP to a multicast group.
+ * @qp: QP to attach to the multicast group. The QP must be a UD QP.
+ * @gid: Multicast group GID.
+ * @lid: Multicast group LID in host byte order.
+ *
+ * In order to route multicast packets correctly, subnet
+ * administration must have created the multicast group and configured
+ * the fabric appropriately. The port associated with the specified
+ * QP must also be a member of the multicast group.
+ */
+int ibv_attach_mcast(struct ibv_qp *qp, const union ibv_gid *gid, uint16_t lid);
+
+/**
+ * ibv_detach_mcast - Detaches the specified QP from a multicast group.
+ * @qp: QP to detach from the multicast group.
+ * @gid: Multicast group GID.
+ * @lid: Multicast group LID in host byte order.
+ */
+int ibv_detach_mcast(struct ibv_qp *qp, const union ibv_gid *gid, uint16_t lid);
+
+/**
+ * ibv_fork_init - Prepare data structures so that fork() may be used
+ * safely. If this function is not called or returns a non-zero
+ * status, then libibverbs data structures are not fork()-safe and the
+ * effect of an application calling fork() is undefined.
+ */
+int ibv_fork_init(void);
+
+/**
+ * ibv_node_type_str - Return string describing node_type enum value
+ */
+const char *ibv_node_type_str(enum ibv_node_type node_type);
+
+/**
+ * ibv_port_state_str - Return string describing port_state enum value
+ */
+const char *ibv_port_state_str(enum ibv_port_state port_state);
+
+/**
+ * ibv_event_type_str - Return string describing event_type enum value
+ */
+const char *ibv_event_type_str(enum ibv_event_type event);
+
+END_C_DECLS
+
+# undef __attribute_const
+
+#include <infiniband/verbs_exp.h>
+
+#endif /* INFINIBAND_VERBS_H */
diff --git a/external_libs/ibverbs/include/infiniband/verbs_exp.h b/external_libs/ibverbs/include/infiniband/verbs_exp.h
new file mode 100644
index 00000000..ae94deb8
--- /dev/null
+++ b/external_libs/ibverbs/include/infiniband/verbs_exp.h
@@ -0,0 +1,3585 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2004, 2011-2012 Intel Corporation. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2005 PathScale, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INFINIBAND_VERBS_EXP_H
+#define INFINIBAND_VERBS_EXP_H
+
+#include <infiniband/verbs.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if __GNUC__ >= 3
+# define __attribute_const __attribute__((const))
+#else
+# define __attribute_const
+#endif
+
+BEGIN_C_DECLS
+
+#define IBV_EXP_RET_ON_INVALID_COMP_MASK(val, valid_mask, ret) \
+ if ((val) > (valid_mask)) { \
+ fprintf(stderr, "%s: invalid comp_mask !!! (comp_mask = 0x%x valid_mask = 0x%x)\n", \
+ __FUNCTION__, val, valid_mask); \
+ errno = EINVAL; \
+ return ret; \
+ }
+
+#define IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(val, valid_mask) \
+ IBV_EXP_RET_ON_INVALID_COMP_MASK(val, valid_mask, NULL)
+
+#define IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(val, valid_mask) \
+ IBV_EXP_RET_ON_INVALID_COMP_MASK(val, valid_mask, EINVAL)
+
+
+#define IBV_EXP_IMPLICIT_MR_SIZE (~((size_t)0))
+
+enum ibv_exp_func_name {
+ IBV_EXP_POST_SEND_FUNC,
+ IBV_EXP_POLL_CQ_FUNC,
+ IBV_POST_SEND_FUNC,
+ IBV_POLL_CQ_FUNC,
+ IBV_POST_RECV_FUNC
+};
+
+enum ibv_exp_start_values {
+ IBV_EXP_START_ENUM = 0x40,
+ IBV_EXP_START_FLAG_LOC = 0x20,
+ IBV_EXP_START_FLAG = (1ULL << IBV_EXP_START_FLAG_LOC),
+};
+
+/*
+ * Capabilities for exp_atomic_cap field in ibv_exp_device_attr struct
+ */
+enum ibv_exp_atomic_cap {
+ IBV_EXP_ATOMIC_NONE = IBV_ATOMIC_NONE,
+ IBV_EXP_ATOMIC_HCA = IBV_ATOMIC_HCA,
+ IBV_EXP_ATOMIC_GLOB = IBV_ATOMIC_GLOB,
+
+ IBV_EXP_ATOMIC_HCA_REPLY_BE = IBV_EXP_START_ENUM /* HOST is LE and atomic reply is BE */
+};
+
+/*
+ * Flags for exp_device_cap_flags field in ibv_exp_device_attr struct
+ */
+enum ibv_exp_device_cap_flags {
+ IBV_EXP_DEVICE_RESIZE_MAX_WR = IBV_DEVICE_RESIZE_MAX_WR,
+ IBV_EXP_DEVICE_BAD_PKEY_CNTR = IBV_DEVICE_BAD_PKEY_CNTR,
+ IBV_EXP_DEVICE_BAD_QKEY_CNTR = IBV_DEVICE_BAD_QKEY_CNTR,
+ IBV_EXP_DEVICE_RAW_MULTI = IBV_DEVICE_RAW_MULTI,
+ IBV_EXP_DEVICE_AUTO_PATH_MIG = IBV_DEVICE_AUTO_PATH_MIG,
+ IBV_EXP_DEVICE_CHANGE_PHY_PORT = IBV_DEVICE_CHANGE_PHY_PORT,
+ IBV_EXP_DEVICE_UD_AV_PORT_ENFORCE = IBV_DEVICE_UD_AV_PORT_ENFORCE,
+ IBV_EXP_DEVICE_CURR_QP_STATE_MOD = IBV_DEVICE_CURR_QP_STATE_MOD,
+ IBV_EXP_DEVICE_SHUTDOWN_PORT = IBV_DEVICE_SHUTDOWN_PORT,
+ IBV_EXP_DEVICE_INIT_TYPE = IBV_DEVICE_INIT_TYPE,
+ IBV_EXP_DEVICE_PORT_ACTIVE_EVENT = IBV_DEVICE_PORT_ACTIVE_EVENT,
+ IBV_EXP_DEVICE_SYS_IMAGE_GUID = IBV_DEVICE_SYS_IMAGE_GUID,
+ IBV_EXP_DEVICE_RC_RNR_NAK_GEN = IBV_DEVICE_RC_RNR_NAK_GEN,
+ IBV_EXP_DEVICE_SRQ_RESIZE = IBV_DEVICE_SRQ_RESIZE,
+ IBV_EXP_DEVICE_N_NOTIFY_CQ = IBV_DEVICE_N_NOTIFY_CQ,
+ IBV_EXP_DEVICE_XRC = IBV_DEVICE_XRC,
+
+ IBV_EXP_DEVICE_DC_TRANSPORT = (IBV_EXP_START_FLAG << 0),
+ IBV_EXP_DEVICE_QPG = (IBV_EXP_START_FLAG << 1),
+ IBV_EXP_DEVICE_UD_RSS = (IBV_EXP_START_FLAG << 2),
+ IBV_EXP_DEVICE_UD_TSS = (IBV_EXP_START_FLAG << 3),
+ IBV_EXP_DEVICE_EXT_ATOMICS = (IBV_EXP_START_FLAG << 4),
+ IBV_EXP_DEVICE_NOP = (IBV_EXP_START_FLAG << 5),
+ IBV_EXP_DEVICE_UMR = (IBV_EXP_START_FLAG << 6),
+ IBV_EXP_DEVICE_ODP = (IBV_EXP_START_FLAG << 7),
+ IBV_EXP_DEVICE_VXLAN_SUPPORT = (IBV_EXP_START_FLAG << 10),
+ IBV_EXP_DEVICE_RX_CSUM_TCP_UDP_PKT = (IBV_EXP_START_FLAG << 11),
+ IBV_EXP_DEVICE_RX_CSUM_IP_PKT = (IBV_EXP_START_FLAG << 12),
+ IBV_EXP_DEVICE_EC_OFFLOAD = (IBV_EXP_START_FLAG << 13),
+ IBV_EXP_DEVICE_EXT_MASKED_ATOMICS = (IBV_EXP_START_FLAG << 14),
+ IBV_EXP_DEVICE_RX_TCP_UDP_PKT_TYPE = (IBV_EXP_START_FLAG << 15),
+ IBV_EXP_DEVICE_SCATTER_FCS = (IBV_EXP_START_FLAG << 16),
+ IBV_EXP_DEVICE_MEM_WINDOW = (IBV_EXP_START_FLAG << 17),
+ IBV_EXP_DEVICE_MEM_MGT_EXTENSIONS = (IBV_EXP_START_FLAG << 21),
+ IBV_EXP_DEVICE_DC_INFO = (IBV_EXP_START_FLAG << 22),
+ /* Jumping to 23 as of next capability in include/rdma/ib_verbs.h */
+ IBV_EXP_DEVICE_MW_TYPE_2A = (IBV_EXP_START_FLAG << 23),
+ IBV_EXP_DEVICE_MW_TYPE_2B = (IBV_EXP_START_FLAG << 24),
+ IBV_EXP_DEVICE_CROSS_CHANNEL = (IBV_EXP_START_FLAG << 28),
+ IBV_EXP_DEVICE_MANAGED_FLOW_STEERING = (IBV_EXP_START_FLAG << 29),
+ IBV_EXP_DEVICE_MR_ALLOCATE = (IBV_EXP_START_FLAG << 30),
+ IBV_EXP_DEVICE_SHARED_MR = (IBV_EXP_START_FLAG << 31),
+};
+
+/*
+ * Flags for ibv_exp_device_attr struct comp_mask.
+ */
+enum ibv_exp_device_attr_comp_mask {
+ IBV_EXP_DEVICE_ATTR_CALC_CAP = (1 << 0),
+ IBV_EXP_DEVICE_ATTR_WITH_TIMESTAMP_MASK = (1 << 1),
+ IBV_EXP_DEVICE_ATTR_WITH_HCA_CORE_CLOCK = (1 << 2),
+ IBV_EXP_DEVICE_ATTR_EXP_CAP_FLAGS = (1 << 3),
+ IBV_EXP_DEVICE_DC_RD_REQ = (1 << 4),
+ IBV_EXP_DEVICE_DC_RD_RES = (1 << 5),
+ IBV_EXP_DEVICE_ATTR_INLINE_RECV_SZ = (1 << 6),
+ IBV_EXP_DEVICE_ATTR_RSS_TBL_SZ = (1 << 7),
+ IBV_EXP_DEVICE_ATTR_EXT_ATOMIC_ARGS = (1 << 8),
+ IBV_EXP_DEVICE_ATTR_UMR = (1 << 9),
+ IBV_EXP_DEVICE_ATTR_ODP = (1 << 10),
+ IBV_EXP_DEVICE_ATTR_MAX_DCT = (1 << 11),
+ IBV_EXP_DEVICE_ATTR_MAX_CTX_RES_DOMAIN = (1 << 12),
+ IBV_EXP_DEVICE_ATTR_RX_HASH = (1 << 13),
+ IBV_EXP_DEVICE_ATTR_MAX_WQ_TYPE_RQ = (1 << 14),
+ IBV_EXP_DEVICE_ATTR_MAX_DEVICE_CTX = (1 << 15),
+ IBV_EXP_DEVICE_ATTR_MP_RQ = (1 << 16),
+ IBV_EXP_DEVICE_ATTR_VLAN_OFFLOADS = (1 << 17),
+ IBV_EXP_DEVICE_ATTR_EC_CAPS = (1 << 18),
+ IBV_EXP_DEVICE_ATTR_MASKED_ATOMICS = (1 << 19),
+ IBV_EXP_DEVICE_ATTR_RX_PAD_END_ALIGN = (1 << 20),
+ IBV_EXP_DEVICE_ATTR_TSO_CAPS = (1 << 21),
+ IBV_EXP_DEVICE_ATTR_PACKET_PACING_CAPS = (1 << 22),
+ /* set supported bits for validity check */
+ IBV_EXP_DEVICE_ATTR_RESERVED = (1 << 23),
+};
+
+struct ibv_exp_device_calc_cap {
+ uint64_t data_types;
+ uint64_t data_sizes;
+ uint64_t int_ops;
+ uint64_t uint_ops;
+ uint64_t fp_ops;
+};
+
+struct ibv_exp_ext_atomics_params {
+ /* defines which masked operation sizes are supported with same
+ * endianness as stated in atomic_cap field
+ */
+ uint64_t log_atomic_arg_sizes; /* bit-mask of supported sizes */
+ uint32_t max_fa_bit_boundary;
+ uint32_t log_max_atomic_inline;
+};
+
+struct ibv_exp_masked_atomic_params {
+ uint32_t max_fa_bit_boundary;
+ uint32_t log_max_atomic_inline;
+ uint64_t masked_log_atomic_arg_sizes;
+ uint64_t masked_log_atomic_arg_sizes_network_endianness;
+};
+
+enum ibv_odp_general_cap_bits {
+ IBV_EXP_ODP_SUPPORT = 1 << 0,
+};
+
+enum ibv_odp_transport_cap_bits {
+ IBV_EXP_ODP_SUPPORT_SEND = 1 << 0,
+ IBV_EXP_ODP_SUPPORT_RECV = 1 << 1,
+ IBV_EXP_ODP_SUPPORT_WRITE = 1 << 2,
+ IBV_EXP_ODP_SUPPORT_READ = 1 << 3,
+ IBV_EXP_ODP_SUPPORT_ATOMIC = 1 << 4,
+ IBV_EXP_ODP_SUPPORT_SRQ_RECV = 1 << 5,
+};
+
+struct ibv_exp_umr_caps {
+ uint32_t max_klm_list_size;
+ uint32_t max_send_wqe_inline_klms;
+ uint32_t max_umr_recursion_depth;
+ uint32_t max_umr_stride_dimension;
+};
+
+struct ibv_exp_odp_caps {
+ uint64_t general_odp_caps;
+ struct {
+ uint32_t rc_odp_caps;
+ uint32_t uc_odp_caps;
+ uint32_t ud_odp_caps;
+ uint32_t dc_odp_caps;
+ uint32_t xrc_odp_caps;
+ uint32_t raw_eth_odp_caps;
+ } per_transport_caps;
+};
+
+enum ibv_exp_supported_qp_types {
+ IBV_EXP_QPT_RC = 1ULL << 0,
+ IBV_EXP_QPT_UC = 1ULL << 1,
+ IBV_EXP_QPT_UD = 1ULL << 2,
+ IBV_EXP_QPT_XRC_INIT = 1ULL << 3,
+ IBV_EXP_QPT_XRC_TGT = 1ULL << 4,
+ IBV_EXP_QPT_RAW_PACKET = 1ULL << 5,
+ IBV_EXP_QPT_RESERVED = 1ULL << 6
+};
+
+struct ibv_exp_rx_hash_caps {
+ uint32_t max_rwq_indirection_tables;
+ uint32_t max_rwq_indirection_table_size;
+ uint8_t supported_hash_functions; /* from ibv_exp_rx_hash_function_flags */
+ uint64_t supported_packet_fields; /* from ibv_exp_rx_hash_fields */
+ uint32_t supported_qps; /* from ibv_exp_supported_qp_types */
+};
+
+enum ibv_exp_mp_rq_shifts {
+ IBV_EXP_MP_RQ_NO_SHIFT = 0,
+ IBV_EXP_MP_RQ_2BYTES_SHIFT = 1 << 0
+};
+
+struct ibv_exp_mp_rq_caps {
+ uint32_t supported_qps; /* use ibv_exp_supported_qp_types */
+ uint32_t allowed_shifts; /* use ibv_exp_mp_rq_shifts */
+ uint8_t min_single_wqe_log_num_of_strides;
+ uint8_t max_single_wqe_log_num_of_strides;
+ uint8_t min_single_stride_log_num_of_bytes;
+ uint8_t max_single_stride_log_num_of_bytes;
+};
+
+struct ibv_exp_ec_caps {
+ uint32_t max_ec_data_vector_count;
+ uint32_t max_ec_calc_inflight_calcs;
+};
+
+#define ibv_is_qpt_supported(caps, qpt) ((caps) & (1 << (qpt)))
+
+struct ibv_exp_tso_caps {
+ uint32_t max_tso;
+ uint32_t supported_qpts;
+};
+
+struct ibv_exp_packet_pacing_caps {
+ uint32_t qp_rate_limit_min;
+ uint32_t qp_rate_limit_max; /* In kbps */
+ uint32_t supported_qpts;
+ uint32_t reserved;
+};
+
+struct ibv_exp_device_attr {
+ char fw_ver[64];
+ uint64_t node_guid;
+ uint64_t sys_image_guid;
+ uint64_t max_mr_size;
+ uint64_t page_size_cap;
+ uint32_t vendor_id;
+ uint32_t vendor_part_id;
+ uint32_t hw_ver;
+ int max_qp;
+ int max_qp_wr;
+ int reserved; /* place holder to align with ibv_device_attr */
+ int max_sge;
+ int max_sge_rd;
+ int max_cq;
+ int max_cqe;
+ int max_mr;
+ int max_pd;
+ int max_qp_rd_atom;
+ int max_ee_rd_atom;
+ int max_res_rd_atom;
+ int max_qp_init_rd_atom;
+ int max_ee_init_rd_atom;
+ enum ibv_exp_atomic_cap exp_atomic_cap;
+ int max_ee;
+ int max_rdd;
+ int max_mw;
+ int max_raw_ipv6_qp;
+ int max_raw_ethy_qp;
+ int max_mcast_grp;
+ int max_mcast_qp_attach;
+ int max_total_mcast_qp_attach;
+ int max_ah;
+ int max_fmr;
+ int max_map_per_fmr;
+ int max_srq;
+ int max_srq_wr;
+ int max_srq_sge;
+ uint16_t max_pkeys;
+ uint8_t local_ca_ack_delay;
+ uint8_t phys_port_cnt;
+ uint32_t comp_mask;
+ struct ibv_exp_device_calc_cap calc_cap;
+ uint64_t timestamp_mask;
+ uint64_t hca_core_clock;
+ uint64_t exp_device_cap_flags; /* use ibv_exp_device_cap_flags */
+ int max_dc_req_rd_atom;
+ int max_dc_res_rd_atom;
+ int inline_recv_sz;
+ uint32_t max_rss_tbl_sz;
+ struct ibv_exp_ext_atomics_params ext_atom;
+ struct ibv_exp_umr_caps umr_caps;
+ struct ibv_exp_odp_caps odp_caps;
+ int max_dct;
+ int max_ctx_res_domain;
+ struct ibv_exp_rx_hash_caps rx_hash_caps;
+ uint32_t max_wq_type_rq;
+ int max_device_ctx;
+ struct ibv_exp_mp_rq_caps mp_rq_caps;
+ uint16_t wq_vlan_offloads_cap; /* use ibv_exp_vlan_offloads enum */
+ struct ibv_exp_ec_caps ec_caps;
+ struct ibv_exp_masked_atomic_params masked_atomic;
+ /*
+ * The alignment of the padding end address.
+ * When RX end of packet padding is enabled the device will pad the end
+ * of RX packet up until the next address which is aligned to the
+ * rx_pad_end_addr_align size.
+ * Expected size for this field is according to system cache line size,
+ * for example 64 or 128. When field is 0 padding is not supported.
+ */
+ int rx_pad_end_addr_align;
+ struct ibv_exp_tso_caps tso_caps;
+ struct ibv_exp_packet_pacing_caps packet_pacing_caps;
+};
+
+enum ibv_exp_access_flags {
+ IBV_EXP_ACCESS_LOCAL_WRITE = IBV_ACCESS_LOCAL_WRITE,
+ IBV_EXP_ACCESS_REMOTE_WRITE = IBV_ACCESS_REMOTE_WRITE,
+ IBV_EXP_ACCESS_REMOTE_READ = IBV_ACCESS_REMOTE_READ,
+ IBV_EXP_ACCESS_REMOTE_ATOMIC = IBV_ACCESS_REMOTE_ATOMIC,
+ IBV_EXP_ACCESS_MW_BIND = IBV_ACCESS_MW_BIND,
+
+ IBV_EXP_ACCESS_ALLOCATE_MR = (IBV_EXP_START_FLAG << 5),
+ IBV_EXP_ACCESS_SHARED_MR_USER_READ = (IBV_EXP_START_FLAG << 6),
+ IBV_EXP_ACCESS_SHARED_MR_USER_WRITE = (IBV_EXP_START_FLAG << 7),
+ IBV_EXP_ACCESS_SHARED_MR_GROUP_READ = (IBV_EXP_START_FLAG << 8),
+ IBV_EXP_ACCESS_SHARED_MR_GROUP_WRITE = (IBV_EXP_START_FLAG << 9),
+ IBV_EXP_ACCESS_SHARED_MR_OTHER_READ = (IBV_EXP_START_FLAG << 10),
+ IBV_EXP_ACCESS_SHARED_MR_OTHER_WRITE = (IBV_EXP_START_FLAG << 11),
+ IBV_EXP_ACCESS_NO_RDMA = (IBV_EXP_START_FLAG << 12),
+ IBV_EXP_ACCESS_MW_ZERO_BASED = (IBV_EXP_START_FLAG << 13),
+ IBV_EXP_ACCESS_ON_DEMAND = (IBV_EXP_START_FLAG << 14),
+ IBV_EXP_ACCESS_RELAXED = (IBV_EXP_START_FLAG << 15),
+ IBV_EXP_ACCESS_PHYSICAL_ADDR = (IBV_EXP_START_FLAG << 16),
+ /* set supported bits for validity check */
+ IBV_EXP_ACCESS_RESERVED = (IBV_EXP_START_FLAG << 17)
+};
+
+/* memory window information struct that is common to types 1 and 2 */
+struct ibv_exp_mw_bind_info {
+ struct ibv_mr *mr;
+ uint64_t addr;
+ uint64_t length;
+ uint64_t exp_mw_access_flags; /* use ibv_exp_access_flags */
+};
+
+/*
+ * Flags for ibv_exp_mw_bind struct comp_mask
+ */
+enum ibv_exp_bind_mw_comp_mask {
+ IBV_EXP_BIND_MW_RESERVED = (1 << 0)
+};
+
+/* type 1 specific info */
+struct ibv_exp_mw_bind {
+ struct ibv_qp *qp;
+ struct ibv_mw *mw;
+ uint64_t wr_id;
+ uint64_t exp_send_flags; /* use ibv_exp_send_flags */
+ struct ibv_exp_mw_bind_info bind_info;
+ uint32_t comp_mask; /* reserved for future growth (must be 0) */
+};
+
+enum ibv_exp_calc_op {
+ IBV_EXP_CALC_OP_ADD = 0,
+ IBV_EXP_CALC_OP_MAXLOC,
+ IBV_EXP_CALC_OP_BAND,
+ IBV_EXP_CALC_OP_BXOR,
+ IBV_EXP_CALC_OP_BOR,
+ IBV_EXP_CALC_OP_NUMBER
+};
+
+enum ibv_exp_calc_data_type {
+ IBV_EXP_CALC_DATA_TYPE_INT = 0,
+ IBV_EXP_CALC_DATA_TYPE_UINT,
+ IBV_EXP_CALC_DATA_TYPE_FLOAT,
+ IBV_EXP_CALC_DATA_TYPE_NUMBER
+};
+
+enum ibv_exp_calc_data_size {
+ IBV_EXP_CALC_DATA_SIZE_64_BIT = 0,
+ IBV_EXP_CALC_DATA_SIZE_NUMBER
+};
+
+enum ibv_exp_wr_opcode {
+ IBV_EXP_WR_RDMA_WRITE = IBV_WR_RDMA_WRITE,
+ IBV_EXP_WR_RDMA_WRITE_WITH_IMM = IBV_WR_RDMA_WRITE_WITH_IMM,
+ IBV_EXP_WR_SEND = IBV_WR_SEND,
+ IBV_EXP_WR_SEND_WITH_IMM = IBV_WR_SEND_WITH_IMM,
+ IBV_EXP_WR_RDMA_READ = IBV_WR_RDMA_READ,
+ IBV_EXP_WR_ATOMIC_CMP_AND_SWP = IBV_WR_ATOMIC_CMP_AND_SWP,
+ IBV_EXP_WR_ATOMIC_FETCH_AND_ADD = IBV_WR_ATOMIC_FETCH_AND_ADD,
+
+ IBV_EXP_WR_SEND_WITH_INV = 8 + IBV_EXP_START_ENUM,
+ IBV_EXP_WR_LOCAL_INV = 10 + IBV_EXP_START_ENUM,
+ IBV_EXP_WR_BIND_MW = 14 + IBV_EXP_START_ENUM,
+ IBV_EXP_WR_TSO = 15 + IBV_EXP_START_ENUM,
+ IBV_EXP_WR_SEND_ENABLE = 0x20 + IBV_EXP_START_ENUM,
+ IBV_EXP_WR_RECV_ENABLE,
+ IBV_EXP_WR_CQE_WAIT,
+ IBV_EXP_WR_EXT_MASKED_ATOMIC_CMP_AND_SWP,
+ IBV_EXP_WR_EXT_MASKED_ATOMIC_FETCH_AND_ADD,
+ IBV_EXP_WR_NOP,
+ IBV_EXP_WR_UMR_FILL,
+ IBV_EXP_WR_UMR_INVALIDATE,
+};
+
+enum ibv_exp_send_flags {
+ IBV_EXP_SEND_FENCE = IBV_SEND_FENCE,
+ IBV_EXP_SEND_SIGNALED = IBV_SEND_SIGNALED,
+ IBV_EXP_SEND_SOLICITED = IBV_SEND_SOLICITED,
+ IBV_EXP_SEND_INLINE = IBV_SEND_INLINE,
+
+ IBV_EXP_SEND_IP_CSUM = (IBV_EXP_START_FLAG << 0),
+ IBV_EXP_SEND_WITH_CALC = (IBV_EXP_START_FLAG << 1),
+ IBV_EXP_SEND_WAIT_EN_LAST = (IBV_EXP_START_FLAG << 2),
+ IBV_EXP_SEND_EXT_ATOMIC_INLINE = (IBV_EXP_START_FLAG << 3),
+};
+
+struct ibv_exp_cmp_swap {
+ uint64_t compare_mask;
+ uint64_t compare_val;
+ uint64_t swap_val;
+ uint64_t swap_mask;
+};
+
+struct ibv_exp_fetch_add {
+ uint64_t add_val;
+ uint64_t field_boundary;
+};
+
+/*
+ * Flags for ibv_exp_send_wr struct comp_mask
+ */
+enum ibv_exp_send_wr_comp_mask {
+ IBV_EXP_SEND_WR_ATTR_RESERVED = 1 << 0
+};
+
+struct ibv_exp_mem_region {
+ uint64_t base_addr;
+ struct ibv_mr *mr;
+ size_t length;
+};
+
+struct ibv_exp_mem_repeat_block {
+ uint64_t base_addr; /* array, size corresponds to ndim */
+ struct ibv_mr *mr;
+ size_t *byte_count; /* array, size corresponds to ndim */
+ size_t *stride; /* array, size corresponds to ndim */
+};
+
+enum ibv_exp_umr_wr_type {
+ IBV_EXP_UMR_MR_LIST,
+ IBV_EXP_UMR_REPEAT
+};
+
+struct ibv_exp_send_wr {
+ uint64_t wr_id;
+ struct ibv_exp_send_wr *next;
+ struct ibv_sge *sg_list;
+ int num_sge;
+ enum ibv_exp_wr_opcode exp_opcode; /* use ibv_exp_wr_opcode */
+ int reserved; /* place holder to align with ibv_send_wr */
+ union {
+ uint32_t imm_data; /* in network byte order */
+ uint32_t invalidate_rkey;
+ } ex;
+ union {
+ struct {
+ uint64_t remote_addr;
+ uint32_t rkey;
+ } rdma;
+ struct {
+ uint64_t remote_addr;
+ uint64_t compare_add;
+ uint64_t swap;
+ uint32_t rkey;
+ } atomic;
+ struct {
+ struct ibv_ah *ah;
+ uint32_t remote_qpn;
+ uint32_t remote_qkey;
+ } ud;
+ } wr;
+ union {
+ union {
+ struct {
+ uint32_t remote_srqn;
+ } xrc;
+ } qp_type;
+
+ uint32_t xrc_remote_srq_num;
+ };
+ union {
+ struct {
+ uint64_t remote_addr;
+ uint32_t rkey;
+ } rdma;
+ struct {
+ uint64_t remote_addr;
+ uint64_t compare_add;
+ uint64_t swap;
+ uint32_t rkey;
+ } atomic;
+ struct {
+ struct ibv_cq *cq;
+ int32_t cq_count;
+ } cqe_wait;
+ struct {
+ struct ibv_qp *qp;
+ int32_t wqe_count;
+ } wqe_enable;
+ } task;
+ union {
+ struct {
+ enum ibv_exp_calc_op calc_op;
+ enum ibv_exp_calc_data_type data_type;
+ enum ibv_exp_calc_data_size data_size;
+ } calc;
+ } op;
+ struct {
+ struct ibv_ah *ah;
+ uint64_t dct_access_key;
+ uint32_t dct_number;
+ } dc;
+ union {
+ struct {
+ struct ibv_mw *mw;
+ uint32_t rkey;
+ struct ibv_exp_mw_bind_info bind_info;
+ } bind_mw;
+ struct {
+ void *hdr;
+ uint16_t hdr_sz;
+ uint16_t mss;
+ } tso;
+ };
+ uint64_t exp_send_flags; /* use ibv_exp_send_flags */
+ uint32_t comp_mask; /* reserved for future growth (must be 0) */
+ union {
+ struct {
+ uint32_t umr_type; /* use ibv_exp_umr_wr_type */
+ struct ibv_exp_mkey_list_container *memory_objects; /* used when IBV_EXP_SEND_INLINE is not set */
+ uint64_t exp_access; /* use ibv_exp_access_flags */
+ struct ibv_mr *modified_mr;
+ uint64_t base_addr;
+ uint32_t num_mrs; /* array size of mem_repeat_block_list or mem_reg_list */
+ union {
+ struct ibv_exp_mem_region *mem_reg_list; /* array, size corresponds to num_mrs */
+ struct {
+ struct ibv_exp_mem_repeat_block *mem_repeat_block_list; /* array, size corresponds to num_mr */
+ size_t *repeat_count; /* array size corresponds to stride_dim */
+ uint32_t stride_dim;
+ } rb;
+ } mem_list;
+ } umr;
+ struct {
+ uint32_t log_arg_sz;
+ uint64_t remote_addr;
+ uint32_t rkey;
+ union {
+ struct {
+ /* For the next four fields:
+ * If operand_size <= 8 then inline data is immediate
+ * from the corresponding field; for small opernands,
+ * ls bits are used.
+ * Else the fields are pointers in the process's address space
+ * where arguments are stored
+ */
+ union {
+ struct ibv_exp_cmp_swap cmp_swap;
+ struct ibv_exp_fetch_add fetch_add;
+ } op;
+ } inline_data; /* IBV_EXP_SEND_EXT_ATOMIC_INLINE is set */
+ /* in the future add support for non-inline argument provisioning */
+ } wr_data;
+ } masked_atomics;
+ } ext_op;
+};
+
+/*
+ * Flags for ibv_exp_values struct comp_mask
+ */
+enum ibv_exp_values_comp_mask {
+ IBV_EXP_VALUES_HW_CLOCK_NS = 1 << 0,
+ IBV_EXP_VALUES_HW_CLOCK = 1 << 1,
+ IBV_EXP_VALUES_RESERVED = 1 << 2
+};
+
+struct ibv_exp_values {
+ uint32_t comp_mask;
+ uint64_t hwclock_ns;
+ uint64_t hwclock;
+};
+
+/*
+ * Flags for flags field in the ibv_exp_cq_init_attr struct
+ */
+enum ibv_exp_cq_create_flags {
+ IBV_EXP_CQ_CREATE_CROSS_CHANNEL = 1 << 0,
+ IBV_EXP_CQ_TIMESTAMP = 1 << 1,
+ IBV_EXP_CQ_TIMESTAMP_TO_SYS_TIME = 1 << 2,
+ IBV_EXP_CQ_COMPRESSED_CQE = 1 << 3,
+ /*
+ * note: update IBV_EXP_CQ_CREATE_FLAGS_MASK when adding new fields
+ */
+};
+
+enum {
+ IBV_EXP_CQ_CREATE_FLAGS_MASK = IBV_EXP_CQ_CREATE_CROSS_CHANNEL |
+ IBV_EXP_CQ_TIMESTAMP |
+ IBV_EXP_CQ_TIMESTAMP_TO_SYS_TIME |
+ IBV_EXP_CQ_COMPRESSED_CQE,
+};
+
+/*
+ * Flags for ibv_exp_cq_init_attr struct comp_mask
+ * Set flags only when relevant field is valid
+ */
+enum ibv_exp_cq_init_attr_mask {
+ IBV_EXP_CQ_INIT_ATTR_FLAGS = 1 << 0,
+ IBV_EXP_CQ_INIT_ATTR_RESERVED = 1 << 1, /* This field is kept for backward compatibility
+ * of application which use the following to set comp_mask:
+ * cq_init_attr.comp_mask = IBV_EXP_CQ_INIT_ATTR_RESERVED - 1
+ * This kind of setting is no longer accepted and application
+ * may set only valid known fields, for example:
+ * cq_init_attr.comp_mask = IBV_EXP_CQ_INIT_ATTR_FLAGS |
+ * IBV_EXP_CQ_INIT_ATTR_RES_DOMAIN
+ */
+ IBV_EXP_CQ_INIT_ATTR_RES_DOMAIN = 1 << 1,
+ IBV_EXP_CQ_INIT_ATTR_PEER_DIRECT = 1 << 2,
+ IBV_EXP_CQ_INIT_ATTR_RESERVED1 = 1 << 3,
+};
+
+struct ibv_exp_res_domain {
+ struct ibv_context *context;
+};
+
+struct ibv_exp_cq_init_attr {
+ uint32_t comp_mask;
+ uint32_t flags;
+ struct ibv_exp_res_domain *res_domain;
+ struct ibv_exp_peer_direct_attr *peer_direct_attrs;
+};
+
+/*
+ * Flags for ibv_exp_ah_attr struct comp_mask
+ */
+enum ibv_exp_ah_attr_attr_comp_mask {
+ IBV_EXP_AH_ATTR_LL = 1 << 0,
+ IBV_EXP_AH_ATTR_VID = 1 << 1,
+ IBV_EXP_AH_ATTR_RESERVED = 1 << 2
+};
+
+enum ll_address_type {
+ LL_ADDRESS_UNKNOWN,
+ LL_ADDRESS_IB,
+ LL_ADDRESS_ETH,
+ LL_ADDRESS_SIZE
+};
+
+struct ibv_exp_ah_attr {
+ struct ibv_global_route grh;
+ uint16_t dlid;
+ uint8_t sl;
+ uint8_t src_path_bits;
+ uint8_t static_rate;
+ uint8_t is_global;
+ uint8_t port_num;
+ uint32_t comp_mask;
+ struct {
+ enum ll_address_type type;
+ uint32_t len;
+ char *address;
+ } ll_address;
+ uint16_t vid;
+};
+
+/*
+ * Flags for exp_attr_mask argument of ibv_exp_modify_qp
+ */
+enum ibv_exp_qp_attr_mask {
+ IBV_EXP_QP_STATE = IBV_QP_STATE,
+ IBV_EXP_QP_CUR_STATE = IBV_QP_CUR_STATE,
+ IBV_EXP_QP_EN_SQD_ASYNC_NOTIFY = IBV_QP_EN_SQD_ASYNC_NOTIFY,
+ IBV_EXP_QP_ACCESS_FLAGS = IBV_QP_ACCESS_FLAGS,
+ IBV_EXP_QP_PKEY_INDEX = IBV_QP_PKEY_INDEX,
+ IBV_EXP_QP_PORT = IBV_QP_PORT,
+ IBV_EXP_QP_QKEY = IBV_QP_QKEY,
+ IBV_EXP_QP_AV = IBV_QP_AV,
+ IBV_EXP_QP_PATH_MTU = IBV_QP_PATH_MTU,
+ IBV_EXP_QP_TIMEOUT = IBV_QP_TIMEOUT,
+ IBV_EXP_QP_RETRY_CNT = IBV_QP_RETRY_CNT,
+ IBV_EXP_QP_RNR_RETRY = IBV_QP_RNR_RETRY,
+ IBV_EXP_QP_RQ_PSN = IBV_QP_RQ_PSN,
+ IBV_EXP_QP_MAX_QP_RD_ATOMIC = IBV_QP_MAX_QP_RD_ATOMIC,
+ IBV_EXP_QP_ALT_PATH = IBV_QP_ALT_PATH,
+ IBV_EXP_QP_MIN_RNR_TIMER = IBV_QP_MIN_RNR_TIMER,
+ IBV_EXP_QP_SQ_PSN = IBV_QP_SQ_PSN,
+ IBV_EXP_QP_MAX_DEST_RD_ATOMIC = IBV_QP_MAX_DEST_RD_ATOMIC,
+ IBV_EXP_QP_PATH_MIG_STATE = IBV_QP_PATH_MIG_STATE,
+ IBV_EXP_QP_CAP = IBV_QP_CAP,
+ IBV_EXP_QP_DEST_QPN = IBV_QP_DEST_QPN,
+
+ IBV_EXP_QP_GROUP_RSS = IBV_EXP_START_FLAG << 21,
+ IBV_EXP_QP_DC_KEY = IBV_EXP_START_FLAG << 22,
+ IBV_EXP_QP_FLOW_ENTROPY = IBV_EXP_START_FLAG << 23,
+ IBV_EXP_QP_RATE_LIMIT = IBV_EXP_START_FLAG << 25,
+};
+
+/*
+ * Flags for ibv_exp_qp_attr struct comp_mask
+ * Set flags only when relevant field is valid
+ */
+enum ibv_exp_qp_attr_comp_mask {
+ IBV_EXP_QP_ATTR_FLOW_ENTROPY = 1UL << 0,
+ IBV_EXP_QP_ATTR_RESERVED = 1UL << 1
+};
+
+struct ibv_exp_qp_attr {
+ enum ibv_qp_state qp_state;
+ enum ibv_qp_state cur_qp_state;
+ enum ibv_mtu path_mtu;
+ enum ibv_mig_state path_mig_state;
+ uint32_t qkey;
+ uint32_t rq_psn;
+ uint32_t sq_psn;
+ uint32_t dest_qp_num;
+ int qp_access_flags; /* use ibv_access_flags form verbs.h */
+ struct ibv_qp_cap cap;
+ struct ibv_ah_attr ah_attr;
+ struct ibv_ah_attr alt_ah_attr;
+ uint16_t pkey_index;
+ uint16_t alt_pkey_index;
+ uint8_t en_sqd_async_notify;
+ uint8_t sq_draining;
+ uint8_t max_rd_atomic;
+ uint8_t max_dest_rd_atomic;
+ uint8_t min_rnr_timer;
+ uint8_t port_num;
+ uint8_t timeout;
+ uint8_t retry_cnt;
+ uint8_t rnr_retry;
+ uint8_t alt_port_num;
+ uint8_t alt_timeout;
+ uint64_t dct_key;
+ uint32_t comp_mask; /* reserved for future growth (must be 0) */
+ uint32_t flow_entropy;
+ uint32_t rate_limit;
+};
+
+/*
+ * Flags for ibv_exp_qp_init_attr struct comp_mask
+ * Set flags only when relevant field is valid
+ */
+enum ibv_exp_qp_init_attr_comp_mask {
+ IBV_EXP_QP_INIT_ATTR_PD = 1 << 0,
+ IBV_EXP_QP_INIT_ATTR_XRCD = 1 << 1,
+ IBV_EXP_QP_INIT_ATTR_CREATE_FLAGS = 1 << 2,
+ IBV_EXP_QP_INIT_ATTR_INL_RECV = 1 << 3,
+ IBV_EXP_QP_INIT_ATTR_QPG = 1 << 4,
+ IBV_EXP_QP_INIT_ATTR_ATOMICS_ARG = 1 << 5,
+ IBV_EXP_QP_INIT_ATTR_MAX_INL_KLMS = 1 << 6,
+ IBV_EXP_QP_INIT_ATTR_RESERVED = 1 << 7, /* This field is kept for backward compatibility
+ * of application which use the following to set comp_mask:
+ * qp_init_attr.comp_mask = IBV_EXP_QP_INIT_ATTR_RESERVED - 1
+ * This kind of setting is no longer accepted and application
+ * may set only valid known fields, for example:
+ * qp_init_attr.comp_mask = IBV_EXP_QP_INIT_ATTR_PD |
+ * IBV_EXP_QP_INIT_ATTR_CREATE_FLAGS
+ */
+ IBV_EXP_QP_INIT_ATTR_RES_DOMAIN = 1 << 7,
+ IBV_EXP_QP_INIT_ATTR_RX_HASH = 1 << 8,
+ IBV_EXP_QP_INIT_ATTR_PORT = 1 << 9,
+ IBV_EXP_QP_INIT_ATTR_PEER_DIRECT = 1 << 10,
+ IBV_EXP_QP_INIT_ATTR_MAX_TSO_HEADER = 1 << 11,
+ IBV_EXP_QP_INIT_ATTR_RESERVED1 = 1 << 12,
+};
+
+enum ibv_exp_qpg_type {
+ IBV_EXP_QPG_NONE = 0,
+ IBV_EXP_QPG_PARENT = (1<<0),
+ IBV_EXP_QPG_CHILD_RX = (1<<1),
+ IBV_EXP_QPG_CHILD_TX = (1<<2)
+};
+
+struct ibv_exp_qpg_init_attrib {
+ uint32_t tss_child_count;
+ uint32_t rss_child_count;
+};
+
+struct ibv_exp_qpg {
+ uint32_t qpg_type;
+ union {
+ struct ibv_qp *qpg_parent; /* see qpg_type */
+ struct ibv_exp_qpg_init_attrib parent_attrib;
+ };
+};
+
+/*
+ * RX Hash Function flags.
+*/
+enum ibv_exp_rx_hash_function_flags {
+ IBV_EXP_RX_HASH_FUNC_TOEPLITZ = 1 << 0,
+ IBV_EXP_RX_HASH_FUNC_XOR = 1 << 1
+};
+
+/*
+ * RX Hash flags, these flags allows to set which incoming packet field should
+ * participates in RX Hash. Each flag represent certain packet's field,
+ * when the flag is set the field that is represented by the flag will
+ * participate in RX Hash calculation.
+ * Notice: *IPV4 and *IPV6 flags can't be enabled together on the same QP
+ * and *TCP and *UDP flags can't be enabled together on the same QP.
+*/
+enum ibv_exp_rx_hash_fields {
+ IBV_EXP_RX_HASH_SRC_IPV4 = 1 << 0,
+ IBV_EXP_RX_HASH_DST_IPV4 = 1 << 1,
+ IBV_EXP_RX_HASH_SRC_IPV6 = 1 << 2,
+ IBV_EXP_RX_HASH_DST_IPV6 = 1 << 3,
+ IBV_EXP_RX_HASH_SRC_PORT_TCP = 1 << 4,
+ IBV_EXP_RX_HASH_DST_PORT_TCP = 1 << 5,
+ IBV_EXP_RX_HASH_SRC_PORT_UDP = 1 << 6,
+ IBV_EXP_RX_HASH_DST_PORT_UDP = 1 << 7
+};
+
+/*
+ * RX Hash QP configuration. Sets hash function, hash types and
+ * Indirection table for QPs with enabled IBV_QP_INIT_ATTR_RX_HASH flag.
+*/
+struct ibv_exp_rx_hash_conf {
+ /* enum ibv_exp_rx_hash_function_flags */
+ uint8_t rx_hash_function;
+ /* valid only for Toeplitz */
+ uint8_t rx_hash_key_len;
+ uint8_t *rx_hash_key;
+ /* enum ibv_exp_rx_hash_fields */
+ uint64_t rx_hash_fields_mask;
+ struct ibv_exp_rwq_ind_table *rwq_ind_tbl;
+};
+
+/*
+ * Flags for exp_create_flags field in ibv_exp_qp_init_attr struct
+ */
+enum ibv_exp_qp_create_flags {
+ IBV_EXP_QP_CREATE_CROSS_CHANNEL = (1 << 2),
+ IBV_EXP_QP_CREATE_MANAGED_SEND = (1 << 3),
+ IBV_EXP_QP_CREATE_MANAGED_RECV = (1 << 4),
+ IBV_EXP_QP_CREATE_IGNORE_SQ_OVERFLOW = (1 << 6),
+ IBV_EXP_QP_CREATE_IGNORE_RQ_OVERFLOW = (1 << 7),
+ IBV_EXP_QP_CREATE_ATOMIC_BE_REPLY = (1 << 8),
+ IBV_EXP_QP_CREATE_UMR = (1 << 9),
+ IBV_EXP_QP_CREATE_EC_PARITY_EN = (1 << 10),
+ IBV_EXP_QP_CREATE_RX_END_PADDING = (1 << 11),
+ IBV_EXP_QP_CREATE_SCATTER_FCS = (1 << 12),
+ IBV_EXP_QP_CREATE_INTERNAL_USE = (1 << 15),
+ /* set supported bits for validity check */
+ IBV_EXP_QP_CREATE_MASK = (0x00001FDC)
+};
+
+struct ibv_exp_qp_init_attr {
+ void *qp_context;
+ struct ibv_cq *send_cq;
+ struct ibv_cq *recv_cq;
+ struct ibv_srq *srq;
+ struct ibv_qp_cap cap;
+ enum ibv_qp_type qp_type;
+ int sq_sig_all;
+
+ uint32_t comp_mask; /* use ibv_exp_qp_init_attr_comp_mask */
+ struct ibv_pd *pd;
+ struct ibv_xrcd *xrcd;
+ uint32_t exp_create_flags; /* use ibv_exp_qp_create_flags */
+
+ uint32_t max_inl_recv;
+ struct ibv_exp_qpg qpg;
+ uint32_t max_atomic_arg;
+ uint32_t max_inl_send_klms;
+ struct ibv_exp_res_domain *res_domain;
+ struct ibv_exp_rx_hash_conf *rx_hash_conf;
+ uint8_t port_num;
+ struct ibv_exp_peer_direct_attr *peer_direct_attrs;
+ uint16_t max_tso_header;
+};
+
+/*
+ * Flags for ibv_exp_dct_init_attr struct comp_mask
+ */
+enum ibv_exp_dct_init_attr_comp_mask {
+ IBV_EXP_DCT_INIT_ATTR_RESERVED = 1 << 0
+};
+
+enum {
+ IBV_EXP_DCT_CREATE_FLAGS_MASK = (1 << 0) - 1,
+};
+
+struct ibv_exp_dct_init_attr {
+ struct ibv_pd *pd;
+ struct ibv_cq *cq;
+ struct ibv_srq *srq;
+ uint64_t dc_key;
+ uint8_t port;
+ uint32_t access_flags; /* use ibv_access_flags form verbs.h */
+ uint8_t min_rnr_timer;
+ uint8_t tclass;
+ uint32_t flow_label;
+ enum ibv_mtu mtu;
+ uint8_t pkey_index;
+ uint8_t gid_index;
+ uint8_t hop_limit;
+ uint32_t inline_size;
+ uint32_t create_flags;
+ uint32_t comp_mask; /* reserved for future growth (must be 0) */
+};
+
+enum {
+ IBV_EXP_DCT_STATE_ACTIVE = 0,
+ IBV_EXP_DCT_STATE_DRAINING = 1,
+ IBV_EXP_DCT_STATE_DRAINED = 2
+};
+
+/*
+ * Flags for ibv_exp_dct_attr struct comp_mask
+ */
+enum ibv_exp_dct_attr_comp_mask {
+ IBV_EXP_DCT_ATTR_RESERVED = 1 << 0
+};
+
+struct ibv_exp_dct_attr {
+ uint64_t dc_key;
+ uint8_t port;
+ uint32_t access_flags; /* use ibv_access_flags form verbs.h */
+ uint8_t min_rnr_timer;
+ uint8_t tclass;
+ uint32_t flow_label;
+ enum ibv_mtu mtu;
+ uint8_t pkey_index;
+ uint8_t gid_index;
+ uint8_t hop_limit;
+ uint32_t key_violations;
+ uint8_t state;
+ struct ibv_srq *srq;
+ struct ibv_cq *cq;
+ struct ibv_pd *pd;
+ uint32_t comp_mask; /* reserved for future growth (must be 0) */
+};
+
+enum {
+ IBV_EXP_QUERY_PORT_STATE = 1 << 0,
+ IBV_EXP_QUERY_PORT_MAX_MTU = 1 << 1,
+ IBV_EXP_QUERY_PORT_ACTIVE_MTU = 1 << 2,
+ IBV_EXP_QUERY_PORT_GID_TBL_LEN = 1 << 3,
+ IBV_EXP_QUERY_PORT_CAP_FLAGS = 1 << 4,
+ IBV_EXP_QUERY_PORT_MAX_MSG_SZ = 1 << 5,
+ IBV_EXP_QUERY_PORT_BAD_PKEY_CNTR = 1 << 6,
+ IBV_EXP_QUERY_PORT_QKEY_VIOL_CNTR = 1 << 7,
+ IBV_EXP_QUERY_PORT_PKEY_TBL_LEN = 1 << 8,
+ IBV_EXP_QUERY_PORT_LID = 1 << 9,
+ IBV_EXP_QUERY_PORT_SM_LID = 1 << 10,
+ IBV_EXP_QUERY_PORT_LMC = 1 << 11,
+ IBV_EXP_QUERY_PORT_MAX_VL_NUM = 1 << 12,
+ IBV_EXP_QUERY_PORT_SM_SL = 1 << 13,
+ IBV_EXP_QUERY_PORT_SUBNET_TIMEOUT = 1 << 14,
+ IBV_EXP_QUERY_PORT_INIT_TYPE_REPLY = 1 << 15,
+ IBV_EXP_QUERY_PORT_ACTIVE_WIDTH = 1 << 16,
+ IBV_EXP_QUERY_PORT_ACTIVE_SPEED = 1 << 17,
+ IBV_EXP_QUERY_PORT_PHYS_STATE = 1 << 18,
+ IBV_EXP_QUERY_PORT_LINK_LAYER = 1 << 19,
+ /* mask of the fields that exists in the standard query_port_command */
+ IBV_EXP_QUERY_PORT_STD_MASK = (1 << 20) - 1,
+ /* mask of all supported fields */
+ IBV_EXP_QUERY_PORT_MASK = IBV_EXP_QUERY_PORT_STD_MASK,
+};
+
+/*
+ * Flags for ibv_exp_port_attr struct comp_mask
+ * Set flags only when relevant field is valid
+ */
+enum ibv_exp_query_port_attr_comp_mask {
+ IBV_EXP_QUERY_PORT_ATTR_MASK1 = 1 << 0,
+ IBV_EXP_QUERY_PORT_ATTR_RESERVED = 1 << 1,
+
+ IBV_EXP_QUERY_PORT_ATTR_MASKS = IBV_EXP_QUERY_PORT_ATTR_RESERVED - 1
+};
+
+struct ibv_exp_port_attr {
+ union {
+ struct {
+ enum ibv_port_state state;
+ enum ibv_mtu max_mtu;
+ enum ibv_mtu active_mtu;
+ int gid_tbl_len;
+ uint32_t port_cap_flags;
+ uint32_t max_msg_sz;
+ uint32_t bad_pkey_cntr;
+ uint32_t qkey_viol_cntr;
+ uint16_t pkey_tbl_len;
+ uint16_t lid;
+ uint16_t sm_lid;
+ uint8_t lmc;
+ uint8_t max_vl_num;
+ uint8_t sm_sl;
+ uint8_t subnet_timeout;
+ uint8_t init_type_reply;
+ uint8_t active_width;
+ uint8_t active_speed;
+ uint8_t phys_state;
+ uint8_t link_layer;
+ uint8_t reserved;
+ };
+ struct ibv_port_attr port_attr;
+ };
+ uint32_t comp_mask;
+ uint32_t mask1;
+};
+
+enum ibv_exp_cq_attr_mask {
+ IBV_EXP_CQ_MODERATION = 1 << 0,
+ IBV_EXP_CQ_CAP_FLAGS = 1 << 1
+};
+
+enum ibv_exp_cq_cap_flags {
+ IBV_EXP_CQ_IGNORE_OVERRUN = (1 << 0),
+ /* set supported bits for validity check */
+ IBV_EXP_CQ_CAP_MASK = (0x00000001)
+};
+
+/*
+ * Flags for ibv_exp_cq_attr struct comp_mask
+ * Set flags only when relevant field is valid
+ */
+enum ibv_exp_cq_attr_comp_mask {
+ IBV_EXP_CQ_ATTR_MODERATION = (1 << 0),
+ IBV_EXP_CQ_ATTR_CQ_CAP_FLAGS = (1 << 1),
+ /* set supported bits for validity check */
+ IBV_EXP_CQ_ATTR_RESERVED = (1 << 2)
+};
+
+struct ibv_exp_cq_attr {
+ uint32_t comp_mask;
+ struct {
+ uint16_t cq_count;
+ uint16_t cq_period;
+ } moderation;
+ uint32_t cq_cap_flags;
+};
+
+enum ibv_exp_rereg_mr_flags {
+ IBV_EXP_REREG_MR_CHANGE_TRANSLATION = IBV_REREG_MR_CHANGE_TRANSLATION,
+ IBV_EXP_REREG_MR_CHANGE_PD = IBV_REREG_MR_CHANGE_PD,
+ IBV_EXP_REREG_MR_CHANGE_ACCESS = IBV_REREG_MR_CHANGE_ACCESS,
+ IBV_EXP_REREG_MR_KEEP_VALID = IBV_REREG_MR_KEEP_VALID,
+ IBV_EXP_REREG_MR_FLAGS_SUPPORTED = ((IBV_EXP_REREG_MR_KEEP_VALID << 1) - 1)
+};
+
+enum ibv_exp_rereg_mr_attr_mask {
+ IBV_EXP_REREG_MR_ATTR_RESERVED = (1 << 0)
+};
+
+struct ibv_exp_rereg_mr_attr {
+ uint32_t comp_mask; /* use ibv_exp_rereg_mr_attr_mask */
+};
+
+/*
+ * Flags for ibv_exp_reg_shared_mr_in struct comp_mask
+ */
+enum ibv_exp_reg_shared_mr_comp_mask {
+ IBV_EXP_REG_SHARED_MR_RESERVED = (1 << 0)
+};
+
+struct ibv_exp_reg_shared_mr_in {
+ uint32_t mr_handle;
+ struct ibv_pd *pd;
+ void *addr;
+ uint64_t exp_access; /* use ibv_exp_access_flags */
+ uint32_t comp_mask; /* reserved for future growth (must be 0) */
+};
+
+enum ibv_exp_flow_flags {
+ IBV_EXP_FLOW_ATTR_FLAGS_ALLOW_LOOP_BACK = 1,
+};
+
+enum ibv_exp_flow_attr_type {
+ /* steering according to rule specifications */
+ IBV_EXP_FLOW_ATTR_NORMAL = 0x0,
+ /* default unicast and multicast rule -
+ * receive all Eth traffic which isn't steered to any QP
+ */
+ IBV_EXP_FLOW_ATTR_ALL_DEFAULT = 0x1,
+ /* default multicast rule -
+ * receive all Eth multicast traffic which isn't steered to any QP
+ */
+ IBV_EXP_FLOW_ATTR_MC_DEFAULT = 0x2,
+ /* sniffer rule - receive all port traffic */
+ IBV_EXP_FLOW_ATTR_SNIFFER = 0x3,
+};
+
+enum ibv_exp_flow_spec_type {
+ IBV_EXP_FLOW_SPEC_ETH = 0x20,
+ IBV_EXP_FLOW_SPEC_IB = 0x21,
+ IBV_EXP_FLOW_SPEC_IPV4 = 0x30,
+ IBV_EXP_FLOW_SPEC_IPV6 = 0x31,
+ IBV_EXP_FLOW_SPEC_IPV4_EXT = 0x32,
+ IBV_EXP_FLOW_SPEC_IPV6_EXT = 0x33,
+ IBV_EXP_FLOW_SPEC_TCP = 0x40,
+ IBV_EXP_FLOW_SPEC_UDP = 0x41,
+ IBV_EXP_FLOW_SPEC_VXLAN_TUNNEL = 0x50,
+ IBV_EXP_FLOW_SPEC_INNER = 0x100,
+ IBV_EXP_FLOW_SPEC_ACTION_TAG = 0x1000,
+};
+
+struct ibv_exp_flow_eth_filter {
+ uint8_t dst_mac[6];
+ uint8_t src_mac[6];
+ uint16_t ether_type;
+ /*
+ * same layout as 802.1q: prio 3, cfi 1, vlan id 12
+ */
+ uint16_t vlan_tag;
+};
+
+struct ibv_exp_flow_spec_eth {
+ enum ibv_exp_flow_spec_type type;
+ uint16_t size;
+ struct ibv_exp_flow_eth_filter val;
+ struct ibv_exp_flow_eth_filter mask;
+};
+
+struct ibv_exp_flow_ib_filter {
+ uint32_t qpn;
+ uint8_t dst_gid[16];
+};
+
+struct ibv_exp_flow_spec_ib {
+ enum ibv_exp_flow_spec_type type;
+ uint16_t size;
+ struct ibv_exp_flow_ib_filter val;
+ struct ibv_exp_flow_ib_filter mask;
+};
+
+struct ibv_exp_flow_ipv4_filter {
+ uint32_t src_ip;
+ uint32_t dst_ip;
+};
+
+struct ibv_exp_flow_spec_ipv4 {
+ enum ibv_exp_flow_spec_type type;
+ uint16_t size;
+ struct ibv_exp_flow_ipv4_filter val;
+ struct ibv_exp_flow_ipv4_filter mask;
+};
+
+struct ibv_exp_flow_ipv6_filter {
+ uint8_t src_ip[16];
+ uint8_t dst_ip[16];
+};
+
+struct ibv_exp_flow_spec_ipv6 {
+ enum ibv_exp_flow_spec_type type;
+ uint16_t size;
+ struct ibv_exp_flow_ipv6_filter val;
+ struct ibv_exp_flow_ipv6_filter mask;
+};
+
+struct ibv_exp_flow_spec_action_tag {
+ enum ibv_exp_flow_spec_type type;
+ uint16_t size;
+ uint32_t tag_id;
+};
+
+struct ibv_exp_flow_ipv6_ext_filter {
+ uint8_t src_ip[16];
+ uint8_t dst_ip[16];
+ uint32_t flow_label;
+ uint8_t next_hdr;
+ uint8_t traffic_class;
+ uint8_t hop_limit;
+};
+
+struct ibv_exp_flow_spec_ipv6_ext {
+ enum ibv_exp_flow_spec_type type;
+ uint16_t size;
+ struct ibv_exp_flow_ipv6_ext_filter val;
+ struct ibv_exp_flow_ipv6_ext_filter mask;
+};
+
+struct ibv_exp_flow_ipv4_ext_filter {
+ uint32_t src_ip;
+ uint32_t dst_ip;
+ uint8_t proto;
+ uint8_t tos;
+ uint8_t ttl;
+ uint8_t flags;
+};
+
+struct ibv_exp_flow_spec_ipv4_ext {
+ enum ibv_exp_flow_spec_type type;
+ uint16_t size;
+ struct ibv_exp_flow_ipv4_ext_filter val;
+ struct ibv_exp_flow_ipv4_ext_filter mask;
+};
+
+struct ibv_exp_flow_tcp_udp_filter {
+ uint16_t dst_port;
+ uint16_t src_port;
+};
+
+struct ibv_exp_flow_spec_tcp_udp {
+ enum ibv_exp_flow_spec_type type;
+ uint16_t size;
+ struct ibv_exp_flow_tcp_udp_filter val;
+ struct ibv_exp_flow_tcp_udp_filter mask;
+};
+
+struct ibv_exp_flow_tunnel_filter {
+ uint32_t tunnel_id;
+};
+
+struct ibv_exp_flow_spec_tunnel {
+ enum ibv_exp_flow_spec_type type;
+ uint16_t size;
+ struct ibv_exp_flow_tunnel_filter val;
+ struct ibv_exp_flow_tunnel_filter mask;
+};
+
+struct ibv_exp_flow_spec {
+ union {
+ struct {
+ uint32_t type;
+ uint16_t size;
+ } hdr;
+ struct ibv_exp_flow_spec_ib ib;
+ struct ibv_exp_flow_spec_eth eth;
+ struct ibv_exp_flow_spec_ipv4 ipv4;
+ struct ibv_exp_flow_spec_ipv4_ext ipv4_ext;
+ struct ibv_exp_flow_spec_tcp_udp tcp_udp;
+ struct ibv_exp_flow_spec_ipv6 ipv6;
+ struct ibv_exp_flow_spec_ipv6_ext ipv6_ext;
+ struct ibv_exp_flow_spec_tunnel tunnel;
+ struct ibv_exp_flow_spec_action_tag flow_tag;
+ };
+};
+
+struct ibv_exp_flow_attr {
+ enum ibv_exp_flow_attr_type type;
+ uint16_t size;
+ uint16_t priority;
+ uint8_t num_of_specs;
+ uint8_t port;
+ uint32_t flags;
+ /* Following are the optional layers according to user request
+ * struct ibv_exp_flow_spec_xxx [L2]
+ * struct ibv_exp_flow_spec_yyy [L3/L4]
+ */
+ uint64_t reserved; /* reserved for future growth (must be 0) */
+};
+
+struct ibv_exp_flow {
+ struct ibv_context *context;
+ uint32_t handle;
+};
+
+struct ibv_exp_dct {
+ struct ibv_context *context;
+ uint32_t handle;
+ uint32_t dct_num;
+ struct ibv_pd *pd;
+ struct ibv_srq *srq;
+ struct ibv_cq *cq;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ uint32_t events_completed;
+};
+
+enum ibv_exp_wc_opcode {
+ IBV_EXP_WC_SEND,
+ IBV_EXP_WC_RDMA_WRITE,
+ IBV_EXP_WC_RDMA_READ,
+ IBV_EXP_WC_COMP_SWAP,
+ IBV_EXP_WC_FETCH_ADD,
+ IBV_EXP_WC_BIND_MW,
+ IBV_EXP_WC_LOCAL_INV = 7,
+ IBV_EXP_WC_MASKED_COMP_SWAP = 9,
+ IBV_EXP_WC_MASKED_FETCH_ADD = 10,
+ IBV_EXP_WC_TSO,
+ IBV_EXP_WC_UMR = 0x100,
+/*
+ * Set value of IBV_EXP_WC_RECV so consumers can test if a completion is a
+ * receive by testing (opcode & IBV_EXP_WC_RECV).
+ */
+ IBV_EXP_WC_RECV = 1 << 7,
+ IBV_EXP_WC_RECV_RDMA_WITH_IMM
+};
+
+enum ibv_exp_wc_flags {
+ IBV_EXP_WC_GRH = IBV_WC_GRH,
+ IBV_EXP_WC_WITH_IMM = IBV_WC_WITH_IMM,
+
+ IBV_EXP_WC_WITH_INV = IBV_EXP_START_FLAG << 2,
+ IBV_EXP_WC_WITH_SL = IBV_EXP_START_FLAG << 4,
+ IBV_EXP_WC_WITH_SLID = IBV_EXP_START_FLAG << 5,
+ IBV_EXP_WC_WITH_TIMESTAMP = IBV_EXP_START_FLAG << 6,
+ IBV_EXP_WC_QP = IBV_EXP_START_FLAG << 7,
+ IBV_EXP_WC_SRQ = IBV_EXP_START_FLAG << 8,
+ IBV_EXP_WC_DCT = IBV_EXP_START_FLAG << 9,
+ IBV_EXP_WC_RX_IP_CSUM_OK = IBV_EXP_START_FLAG << 10,
+ IBV_EXP_WC_RX_TCP_UDP_CSUM_OK = IBV_EXP_START_FLAG << 11,
+ IBV_EXP_WC_RX_IPV4_PACKET = IBV_EXP_START_FLAG << 12,
+ IBV_EXP_WC_RX_IPV6_PACKET = IBV_EXP_START_FLAG << 13,
+ IBV_EXP_WC_RX_TUNNEL_PACKET = IBV_EXP_START_FLAG << 14,
+ IBV_EXP_WC_RX_OUTER_IP_CSUM_OK = IBV_EXP_START_FLAG << 15,
+ IBV_EXP_WC_RX_OUTER_TCP_UDP_CSUM_OK = IBV_EXP_START_FLAG << 16,
+ IBV_EXP_WC_RX_OUTER_IPV4_PACKET = IBV_EXP_START_FLAG << 17,
+ IBV_EXP_WC_RX_OUTER_IPV6_PACKET = IBV_EXP_START_FLAG << 18,
+};
+
+struct ibv_exp_wc {
+ uint64_t wr_id;
+ enum ibv_wc_status status;
+ enum ibv_exp_wc_opcode exp_opcode;
+ uint32_t vendor_err;
+ uint32_t byte_len;
+ uint32_t imm_data; /* in network byte order */
+ uint32_t qp_num;
+ uint32_t src_qp;
+ int reserved; /* place holder to align with ibv_wc */
+ uint16_t pkey_index;
+ uint16_t slid; /* invalid when TS is used */
+ uint8_t sl; /* invalid when TS is used */
+ uint8_t dlid_path_bits;
+ uint64_t timestamp;
+ struct ibv_qp *qp;
+ struct ibv_srq *srq;
+ struct ibv_exp_dct *dct;
+ uint64_t exp_wc_flags; /* use ibv_exp_wc_flags */
+};
+
+/*
+ * Flags for ibv_exp_prefetch_mr comp_mask
+ */
+enum ibv_exp_prefetch_attr_comp_mask {
+ IBV_EXP_PREFETCH_MR_RESERVED = (1 << 0),
+};
+
+/*
+ * Flags for ibv_exp_prefetch_mr flags
+ */
+enum ibv_exp_prefetch_attr_flags {
+ /* request prefetching for write access. Used for both local and remote */
+ IBV_EXP_PREFETCH_WRITE_ACCESS = (1 << 0),
+};
+
+struct ibv_exp_prefetch_attr {
+ /* Use enum ibv_exp_prefetch_attr_flags */
+ uint32_t flags;
+ /* Address of the area to prefetch */
+ void *addr;
+ /* Length of the area to prefetch */
+ size_t length;
+ uint32_t comp_mask;
+};
+
+/*
+ * Flags for ibv_exp_reg_mr_in struct comp_mask
+ */
+enum ibv_exp_reg_mr_in_comp_mask {
+ /* set supported bits for validity check */
+ IBV_EXP_REG_MR_CREATE_FLAGS = (1 << 0),
+ IBV_EXP_REG_MR_RESERVED = (1 << 1)
+};
+
+enum ibv_exp_reg_mr_create_flags {
+ IBV_EXP_REG_MR_CREATE_CONTIG = (1 << 0) /* register mr with contiguous pages */
+};
+
+struct ibv_exp_reg_mr_in {
+ struct ibv_pd *pd;
+ void *addr;
+ size_t length;
+ uint64_t exp_access; /* use ibv_exp_access_flags */
+ uint32_t comp_mask; /* reserved for future growth (must be 0) */
+ uint32_t create_flags; /* use ibv_exp_reg_mr_create_flags */
+};
+
+
+enum ibv_exp_task_type {
+ IBV_EXP_TASK_SEND = 0,
+ IBV_EXP_TASK_RECV = 1
+};
+
+/*
+ * Flags for ibv_exp_task struct comp_mask
+ */
+enum ibv_exp_task_comp_mask {
+ IBV_EXP_TASK_RESERVED = (1 << 0)
+};
+
+struct ibv_exp_task {
+ enum ibv_exp_task_type task_type;
+ struct {
+ struct ibv_qp *qp;
+ union {
+ struct ibv_exp_send_wr *send_wr;
+ struct ibv_recv_wr *recv_wr;
+ };
+ } item;
+ struct ibv_exp_task *next;
+ uint32_t comp_mask; /* reserved for future growth (must be 0) */
+};
+
+/*
+ * Flags for ibv_exp_arm_attr struct comp_mask
+ */
+enum ibv_exp_arm_attr_comp_mask {
+ IBV_EXP_ARM_ATTR_RESERVED = (1 << 0)
+};
+struct ibv_exp_arm_attr {
+ uint32_t comp_mask; /* reserved for future growth (must be 0) */
+};
+
+enum ibv_exp_mr_create_flags {
+ IBV_EXP_MR_SIGNATURE_EN = (1 << 0),
+ IBV_EXP_MR_INDIRECT_KLMS = (1 << 1)
+};
+
+struct ibv_exp_mr_init_attr {
+ uint32_t max_klm_list_size; /* num of entries */
+ uint32_t create_flags; /* use ibv_exp_mr_create_flags */
+ uint64_t exp_access_flags; /* use ibv_exp_access_flags */
+};
+
+/*
+ * Comp_mask for ibv_exp_create_mr_in struct comp_mask
+ */
+enum ibv_exp_create_mr_in_comp_mask {
+ IBV_EXP_CREATE_MR_IN_RESERVED = (1 << 0)
+};
+
+struct ibv_exp_create_mr_in {
+ struct ibv_pd *pd;
+ struct ibv_exp_mr_init_attr attr;
+ uint32_t comp_mask; /* use ibv_exp_create_mr_in_comp_mask */
+};
+
+/*
+ * Flags for ibv_exp_mkey_attr struct comp_mask
+ */
+enum ibv_exp_mkey_attr_comp_mask {
+ IBV_EXP_MKEY_ATTR_RESERVED = (1 << 0)
+};
+
+struct ibv_exp_mkey_attr {
+ uint32_t max_klm_list_size;
+ uint32_t comp_mask; /* use ibv_exp_mkey_attr_comp_mask */
+};
+
+struct ibv_exp_mkey_list_container {
+ uint32_t max_klm_list_size;
+ struct ibv_context *context;
+};
+
+enum ibv_exp_mkey_list_type {
+ IBV_EXP_MKEY_LIST_TYPE_INDIRECT_MR
+};
+
+/*
+ * Flags for ibv_exp_mkey_list_container_attr struct comp_mask
+ */
+enum ibv_exp_alloc_mkey_list_comp_mask {
+ IBV_EXP_MKEY_LIST_CONTAINER_RESERVED = (1 << 0)
+};
+
+struct ibv_exp_mkey_list_container_attr {
+ struct ibv_pd *pd;
+ uint32_t mkey_list_type; /* use ibv_exp_mkey_list_type */
+ uint32_t max_klm_list_size;
+ uint32_t comp_mask; /*use ibv_exp_alloc_mkey_list_comp_mask */
+};
+
+/*
+ * Flags for ibv_exp_rereg_out struct comp_mask
+ */
+enum ibv_exp_rereg_mr_comp_mask {
+ IBV_EXP_REREG_MR_RESERVED = (1 << 0)
+};
+
+struct ibv_exp_rereg_out {
+ int need_dofork;
+ uint32_t comp_mask; /* use ibv_exp_rereg_mr_comp_mask */
+};
+
+/*
+ * Flags for ibv_exp_dereg_out struct comp_mask
+ */
+enum ibv_exp_dereg_mr_comp_mask {
+ IBV_EXP_DEREG_MR_RESERVED = (1 << 0)
+};
+
+struct ibv_exp_dereg_out {
+ int need_dofork;
+ uint32_t comp_mask; /* use ibv_exp_dereg_mr_comp_mask */
+};
+
+struct verbs_env_item {
+ char *name;
+ char *value;
+ struct verbs_env_item *next;
+};
+
+struct verbs_environment {
+ struct verbs_env_item *head;
+ pthread_mutex_t mtx;
+};
+
+/* RSS stuff */
+
+enum ibv_exp_wq_type {
+ IBV_EXP_WQT_RQ,
+ IBV_EXP_WQT_SRQ
+};
+
+enum ibv_exp_wq_state {
+ IBV_EXP_WQS_RESET,
+ IBV_EXP_WQS_RDY,
+ IBV_EXP_WQS_ERR,
+ IBV_EXP_WQS_UNKNOWN
+};
+
+/* VLAN Offloads */
+enum ibv_exp_vlan_offloads {
+ /* Represents C-VLAN stripping feature */
+ IBV_EXP_RECEIVE_WQ_CVLAN_STRIP = (1 << 0),
+ /* Represents C-VLAN insertion feature*/
+ IBV_EXP_RECEIVE_WQ_CVLAN_INSERTION = (1 << 1),
+ IBV_EXP_RECEIVE_WQ_VLAN_OFFLOADS_RESERVED = (1 << 2),
+};
+
+/*
+ * Work Queue. QP can be created without internal WQs "packaged" inside it,
+ * this QPs can be configured to use "external" WQ object as its
+ * receive/send queue.
+ * WQ associated (many to one) with Completion Queue it owns WQ properties
+ * (PD, WQ size etc).
+ * WQ of type IBV_EXP_WQT_RQ contains receive WQEs, in which case its PD serves
+ * scatter as well.
+ * WQ of type IBV_EXP_WQT_SRQ is associated (many to one) with regular ibv_srq,
+ * in which case it does not hold receive WQEs.
+ * QPs can be associated with IBV_EXP_WQT_S/RQ WQs via WQ Indirection Table.
+ */
+struct ibv_exp_wq {
+ struct ibv_context *context;
+ void *wq_context; /* Associated Context of the WQ */
+ uint32_t handle;
+ /* Protection domain WQ should be associated with */
+ struct ibv_pd *pd;
+ /* CQ to be associated with the WQ */
+ struct ibv_cq *cq;
+ /* SRQ handle if WQ is to be associated with an SRQ, otherwise NULL */
+ struct ibv_srq *srq;
+ uint32_t wq_num;
+ enum ibv_exp_wq_state state;
+ enum ibv_exp_wq_type wq_type;
+ uint32_t comp_mask;
+};
+
+enum ibv_exp_wq_init_attr_mask {
+ IBV_EXP_CREATE_WQ_RES_DOMAIN = (1 << 0),
+ IBV_EXP_CREATE_WQ_MP_RQ = (1 << 1),
+ IBV_EXP_CREATE_WQ_VLAN_OFFLOADS = (1 << 2),
+ IBV_EXP_CREATE_WQ_FLAGS = (1 << 3),
+ IBV_EXP_CREATE_WQ_RESERVED = (1 << 4)
+};
+
+struct ibv_exp_wq_mp_rq {
+ enum ibv_exp_mp_rq_shifts use_shift;
+ uint8_t single_wqe_log_num_of_strides;
+ uint8_t single_stride_log_num_of_bytes;
+};
+
+enum ibv_exp_wq_init_attr_flags {
+ IBV_EXP_CREATE_WQ_FLAG_RX_END_PADDING = (1ULL << 0),
+ IBV_EXP_CREATE_WQ_FLAG_SCATTER_FCS = (1ULL << 1),
+ IBV_EXP_CREATE_WQ_FLAG_RESERVED = (1ULL << 2)
+};
+
+struct ibv_exp_wq_init_attr {
+ /* Associated Context of the WQ */
+ void *wq_context;
+ enum ibv_exp_wq_type wq_type;
+ /* Valid for non IBV_EXP_WQT_SRQ WQ */
+ uint32_t max_recv_wr;
+ /* Valid for non IBV_EXP_WQT_SRQ WQ */
+ uint32_t max_recv_sge;
+ /* Protection domain WQ should be associated with */
+ struct ibv_pd *pd;
+ /* CQ to be associated with the WQ */
+ struct ibv_cq *cq;
+ /* SRQ handle if WQ is of type IBV_EXP_WQT_SRQ, otherwise NULL */
+ struct ibv_srq *srq;
+ /* refers to ibv_exp_wq_init_attr_mask */
+ uint32_t comp_mask;
+ struct ibv_exp_res_domain *res_domain;
+ struct ibv_exp_wq_mp_rq mp_rq;
+ uint16_t vlan_offloads; /* use ibv_exp_vlan_offloads enum */
+ uint64_t flags; /* general wq create flags */
+};
+
+enum ibv_exp_wq_attr_mask {
+ IBV_EXP_WQ_ATTR_STATE = 1 << 0,
+ IBV_EXP_WQ_ATTR_CURR_STATE = 1 << 1,
+ IBV_EXP_WQ_ATTR_VLAN_OFFLOADS = 1 << 2,
+ IBV_EXP_WQ_ATTR_RESERVED = 1 << 3
+};
+
+struct ibv_exp_wq_attr {
+ /* enum ibv_exp_wq_attr_mask */
+ uint32_t attr_mask;
+ /* Move the RQ to this state */
+ enum ibv_exp_wq_state wq_state;
+ /* Assume this is the current RQ state */
+ enum ibv_exp_wq_state curr_wq_state;
+ uint16_t vlan_offloads; /* use ibv_exp_vlan_offloads enum */
+};
+
+/*
+ * Receive Work Queue Indirection Table.
+ * It's used in order to distribute incoming packets between different
+ * Receive Work Queues. Associating Receive WQs with different CPU cores
+ * allows to workload the traffic between different CPU cores.
+ * The Indirection Table can contain only WQs of type IBV_EXP_WQT_S/RQ.
+*/
+struct ibv_exp_rwq_ind_table {
+ struct ibv_context *context;
+ struct ibv_pd *pd;
+ int ind_tbl_handle;
+ int ind_tbl_num;
+ uint32_t comp_mask;
+};
+
+enum ibv_exp_ind_table_init_attr_mask {
+ IBV_EXP_CREATE_IND_TABLE_RESERVED = (1 << 0)
+};
+
+/*
+ * Receive Work Queue Indirection Table attributes
+*/
+struct ibv_exp_rwq_ind_table_init_attr {
+ struct ibv_pd *pd;
+ /* Log, base 2, of Indirection table size */
+ uint32_t log_ind_tbl_size;
+ /* Each entry is a pointer to Receive Work Queue */
+ struct ibv_exp_wq **ind_tbl;
+ uint32_t comp_mask;
+};
+
+/* Accelerated verbs */
+enum ibv_exp_thread_model {
+ IBV_EXP_THREAD_SAFE, /* The lib responsible to protect the object in multithreaded environment */
+ IBV_EXP_THREAD_UNSAFE, /* The application responsible to protect the object in multithreaded environment */
+ IBV_EXP_THREAD_SINGLE /* The object is called from only one thread */
+};
+
+enum ibv_exp_msg_model {
+ IBV_EXP_MSG_DEFAULT, /* Use the provider default message model */
+ IBV_EXP_MSG_LOW_LATENCY, /* Hint the provider to optimize for low latency */
+ IBV_EXP_MSG_HIGH_BW, /* Hint the provider to optimize for high bandwidth */
+ IBV_EXP_MSG_FORCE_LOW_LATENCY, /* Force the provider to optimize for low latency */
+};
+
+/*
+ * Resource domains
+ */
+enum ibv_exp_res_domain_init_attr_comp_mask {
+ IBV_EXP_RES_DOMAIN_THREAD_MODEL = (1 << 0),
+ IBV_EXP_RES_DOMAIN_MSG_MODEL = (1 << 1),
+ IBV_EXP_RES_DOMAIN_RESERVED = (1 << 2),
+};
+
+struct ibv_exp_res_domain_init_attr {
+ uint32_t comp_mask; /* use ibv_exp_res_domain_init_attr_comp_mask */
+ enum ibv_exp_thread_model thread_model;
+ enum ibv_exp_msg_model msg_model;
+};
+
+enum ibv_exp_destroy_res_domain_comp_mask {
+ IBV_EXP_DESTROY_RES_DOMAIN_RESERVED = (1 << 0),
+};
+
+struct ibv_exp_destroy_res_domain_attr {
+ uint32_t comp_mask; /* use ibv_exp_destroy_res_domain_comp_mask */
+};
+
+/*
+ * Query interface (specialized Verbs)
+ */
+
+enum ibv_exp_query_intf_flags {
+ /* Interface functions includes correctness and validity checks */
+ IBV_EXP_QUERY_INTF_FLAG_ENABLE_CHECKS = (1 << 0),
+};
+
+enum ibv_exp_intf_family {
+ IBV_EXP_INTF_QP_BURST,
+ IBV_EXP_INTF_CQ,
+ IBV_EXP_INTF_WQ,
+ IBV_EXP_INTF_RESERVED,
+};
+
+enum ibv_exp_experimental_intf_family {
+ IBV_EXP_EXPERIMENTAL_INTF_RESERVED,
+};
+
+enum ibv_exp_intf_scope {
+ IBV_EXP_INTF_GLOBAL, /* Permanent interface, identified by
+ * the ibv_exp_intf_family enum
+ */
+ IBV_EXP_INTF_EXPERIMENTAL, /* Interface under evaluation, identified by
+ * the ibv_exp_experimental_intf_family enum
+ * This interface may change between lib
+ * versions
+ */
+ IBV_EXP_INTF_VENDOR, /* Vendor specific interface, defined in vendor
+ * separate header file
+ */
+ IBV_EXP_INTF_VENDOR_EXPERIMENTAL, /* Vendor interface under evaluation,
+ * defined in vendor separate header
+ * file
+ */
+};
+
+/* Return status from ibv_exp_query_intf */
+enum ibv_exp_query_intf_status {
+ IBV_EXP_INTF_STAT_OK,
+ IBV_EXP_INTF_STAT_VENDOR_NOT_SUPPORTED, /* The provided 'vendor_guid' is not supported */
+ IBV_EXP_INTF_STAT_INTF_NOT_SUPPORTED, /* The provided 'intf' is not supported */
+ IBV_EXP_INTF_STAT_VERSION_NOT_SUPPORTED, /* The provided 'intf_version' is not supported */
+ IBV_EXP_INTF_STAT_INVAL_PARARM, /* General invalid parameter */
+ IBV_EXP_INTF_STAT_INVAL_OBJ_STATE, /* QP is not in INIT, RTR or RTS state */
+ IBV_EXP_INTF_STAT_INVAL_OBJ, /* Mismatch between the provided 'obj'(CQ/QP/WQ) and requested 'intf' */
+ IBV_EXP_INTF_STAT_FLAGS_NOT_SUPPORTED, /* The provided set of 'flags' is not supported */
+ IBV_EXP_INTF_STAT_FAMILY_FLAGS_NOT_SUPPORTED, /* The provided set of 'family_flags' is not supported */
+};
+
+enum ibv_exp_query_intf_comp_mask {
+ IBV_EXP_QUERY_INTF_RESERVED = (1 << 0),
+};
+
+struct ibv_exp_query_intf_params {
+ uint32_t flags; /* use ibv_exp_query_intf_flags */
+ enum ibv_exp_intf_scope intf_scope;
+ uint64_t vendor_guid; /* set in case VENDOR intf_scope selected */
+ uint32_t intf; /* for GLOBAL intf_scope use ibv_exp_intf_family enum */
+ uint32_t intf_version; /* Version */
+ void *obj; /* interface object (CQ/QP/WQ) */
+ void *family_params; /* Family-specific params */
+ uint32_t family_flags; /* Family-specific flags */
+ uint32_t comp_mask; /* use ibv_exp_query_intf_comp_mask */
+};
+
+enum ibv_exp_release_intf_comp_mask {
+ IBV_EXP_RELEASE_INTF_RESERVED = (1 << 0),
+};
+
+struct ibv_exp_release_intf_params {
+ uint32_t comp_mask; /* use ibv_exp_release_intf_comp_mask */
+};
+
+/*
+ * Family interfaces
+ */
+
+/* QP burst family */
+
+/* Flags to use in family_flags field of ibv_exp_query_intf_params on family creation */
+enum ibv_exp_qp_burst_family_create_flags {
+ /* To disable loop-back of multi-cast messages in RAW-ETH */
+ IBV_EXP_QP_BURST_CREATE_DISABLE_ETH_LOOPBACK = (1 << 0),
+ /* To enable Multi-Packet send WR when possible */
+ IBV_EXP_QP_BURST_CREATE_ENABLE_MULTI_PACKET_SEND_WR = (1 << 1),
+};
+
+/* Flags to use on send functions of QP burst family */
+enum ibv_exp_qp_burst_family_flags {
+ IBV_EXP_QP_BURST_SIGNALED = 1 << 0,
+ IBV_EXP_QP_BURST_SOLICITED = 1 << 1,
+ IBV_EXP_QP_BURST_IP_CSUM = 1 << 2,
+ IBV_EXP_QP_BURST_TUNNEL = 1 << 3,
+ IBV_EXP_QP_BURST_FENCE = 1 << 4,
+};
+
+/* All functions of QP family included in QP family version 1 */
+struct ibv_exp_qp_burst_family {
+ int (*send_pending)(struct ibv_qp *qp, uint64_t addr, uint32_t length, uint32_t lkey, uint32_t flags);
+ int (*send_pending_inline)(struct ibv_qp *qp, void *addr, uint32_t length, uint32_t flags);
+ int (*send_pending_sg_list)(struct ibv_qp *qp, struct ibv_sge *sg_list, uint32_t num, uint32_t flags);
+ int (*send_flush)(struct ibv_qp *qp);
+ int (*send_burst)(struct ibv_qp *qp, struct ibv_sge *msg_list, uint32_t num, uint32_t flags);
+ int (*recv_burst)(struct ibv_qp *qp, struct ibv_sge *msg_list, uint32_t num);
+};
+
+struct ibv_exp_qp_burst_family_v1 {
+ /*
+ * send_pending - Put one message in the provider send queue.
+ *
+ * Common usage: After calling several times to send_pending
+ * the application need to call send_flush to ensure the send
+ * of the pending messages.
+ * Note: Use ibv_exp_qp_burst_family_flags for the flags field
+ */
+ int (*send_pending)(struct ibv_qp *qp, uint64_t addr, uint32_t length, uint32_t lkey, uint32_t flags);
+ /*
+ * send_pending_inline - Put one inline message in the provider send queue.
+ *
+ * Common usage: Same as send_pending
+ * Notes:
+ * - The message length must fit the max inline size of the QP.
+ * Providing bigger messages may lead to data corruption and
+ * segmentation fault.
+ * - Use ibv_exp_qp_burst_family_flags for the flags field
+ */
+ int (*send_pending_inline)(struct ibv_qp *qp, void *addr, uint32_t length, uint32_t flags);
+ /*
+ * send_pending_sg_list - Put one scatter-gather(sg) message in the provider send queue.
+ *
+ * Common usage: Same as send_pending
+ * Notes:
+ * - The number of sg entries must fit the max_send_sge of the QP.
+ * Providing bigger list of sg entries may lead to data corruption and
+ * segmentation fault.
+ * - Use ibv_exp_qp_burst_family_flags for the flags field
+ */
+ int (*send_pending_sg_list)(struct ibv_qp *qp, struct ibv_sge *sg_list, uint32_t num, uint32_t flags);
+ /*
+ * send_flush - To flush the pending messages.
+ *
+ * Note: Use ibv_exp_qp_burst_family_flags for the flags field
+ */
+ int (*send_flush)(struct ibv_qp *qp);
+ /*
+ * send_burst - Send a list of 'num' messages (no send_flush required in this case)
+ */
+ int (*send_burst)(struct ibv_qp *qp, struct ibv_sge *msg_list, uint32_t num, uint32_t flags);
+ /*
+ * recv_burst - Post a set of 'num' receive buffers.
+ *
+ * Note: One sge per message is supported by this function
+ */
+ int (*recv_burst)(struct ibv_qp *qp, struct ibv_sge *msg_list, uint32_t num);
+ /*
+ * send_pending_vlan - Put one message in the provider send queue
+ * and insert vlan_tci to header.
+ *
+ * Common usage: Same as send_pending
+ * Note:
+ * - Same as send_pending
+ * - Not supported when MP enable.
+ */
+ int (*send_pending_vlan)(struct ibv_qp *qp, uint64_t addr, uint32_t length,
+ uint32_t lkey, uint32_t flags, uint16_t *vlan_tci);
+ /*
+ * send_pending_inline - Put one inline message in the provider send queue
+ * and insert vlan_tci to header.
+ *
+ * Common usage: Same as send_pending_inline
+ * Notes:
+ * - Same as send_pending_inline
+ * - Not supported when MP enable.
+ */
+ int (*send_pending_inline_vlan)(struct ibv_qp *qp, void *addr, uint32_t length,
+ uint32_t flags, uint16_t *vlan_tci);
+ /*
+ * send_pending_sg_list - Put one scatter-gather(sg) message in the provider send queue
+ * and insert vlan_tci to header.
+ *
+ * Common usage: Same as send_pending_sg_list
+ * Notes:
+ * - Same as send_pending_sg_list
+ * - Not supported when MP enable.
+ */
+ int (*send_pending_sg_list_vlan)(struct ibv_qp *qp, struct ibv_sge *sg_list, uint32_t num,
+ uint32_t flags, uint16_t *vlan_tci);
+};
+
+/* WQ family */
+struct ibv_exp_wq_family {
+ /*
+ * recv_sg_list - Post one scatter-gather(sg) receive buffer.
+ *
+ * Note:
+ * - The number of sg entries must fit the max_recv_sge of the WQ.
+ * Providing bigger list of sg entries may lead to data corruption and
+ * segmentation fault.
+ */
+ int (*recv_sg_list)(struct ibv_exp_wq *wq, struct ibv_sge *sg_list, uint32_t num_sg);
+ /*
+ * recv_burst - Post a set of 'num' receive buffers.
+ *
+ * Note: One sge per message is supported by this function
+ */
+ int (*recv_burst)(struct ibv_exp_wq *wq, struct ibv_sge *msg_list, uint32_t num);
+};
+
+/* CQ family */
+enum ibv_exp_cq_family_flags {
+ /* RX offloads flags */
+ /* The cq_family_flags are applicable
+ * according to the existence of the
+ * related device capabilities flags */
+ IBV_EXP_CQ_RX_IP_CSUM_OK = 1 << 0, /* IBV_EXP_DEVICE_RX_CSUM_IP_PKT or IBV_EXP_DEVICE_RX_CSUM_TCP_UDP_PKT */
+ IBV_EXP_CQ_RX_TCP_UDP_CSUM_OK = 1 << 1, /* IBV_EXP_DEVICE_RX_CSUM_TCP_UDP_PKT */
+ IBV_EXP_CQ_RX_IPV4_PACKET = 1 << 2, /* IBV_EXP_DEVICE_RX_CSUM_IP_PKT or IBV_EXP_DEVICE_RX_CSUM_TCP_UDP_PKT */
+ IBV_EXP_CQ_RX_IPV6_PACKET = 1 << 3, /* IBV_EXP_DEVICE_RX_CSUM_IP_PKT or IBV_EXP_DEVICE_RX_CSUM_TCP_UDP_PKT */
+ IBV_EXP_CQ_RX_TUNNEL_PACKET = 1 << 4, /* IBV_EXP_DEVICE_VXLAN_SUPPORT */
+ IBV_EXP_CQ_RX_OUTER_IP_CSUM_OK = 1 << 5, /* IBV_EXP_DEVICE_VXLAN_SUPPORT */
+ IBV_EXP_CQ_RX_OUTER_TCP_UDP_CSUM_OK = 1 << 6, /* IBV_EXP_DEVICE_VXLAN_SUPPORT */
+ IBV_EXP_CQ_RX_OUTER_IPV4_PACKET = 1 << 7, /* IBV_EXP_DEVICE_VXLAN_SUPPORT */
+ IBV_EXP_CQ_RX_OUTER_IPV6_PACKET = 1 << 8, /* IBV_EXP_DEVICE_VXLAN_SUPPORT */
+
+ /* Flags supported from CQ family version 1 */
+ /* Multi-Packet RQ flag */
+ IBV_EXP_CQ_RX_MULTI_PACKET_LAST_V1 = 1 << 9, /* Last packet on WR */
+ /* CVLAN stripping RQ flag */
+ IBV_EXP_CQ_RX_CVLAN_STRIPPED_V1 = 1 << 10, /*
+ * When set, CVLAN is stripped
+ * from incoming packets.
+ */
+
+ /* The RX TCP/UDP packet flags
+ * applicable according to
+ * IBV_EXP_DEVICE_RX_TCP_UDP_PKT_TYPE
+ * device capabilities flag
+ */
+ IBV_EXP_CQ_RX_TCP_PACKET = 1 << 11,
+ IBV_EXP_CQ_RX_UDP_PACKET = 1 << 12,
+};
+
+/* All functions of CQ family included in CQ family version 1 */
+struct ibv_exp_cq_family {
+ int32_t (*poll_cnt)(struct ibv_cq *cq, uint32_t max);
+ int32_t (*poll_length)(struct ibv_cq *cq, void *buf, uint32_t *inl);
+ int32_t (*poll_length_flags)(struct ibv_cq *cq, void *buf, uint32_t *inl, uint32_t *flags);
+};
+
+struct ibv_exp_cq_family_v1 {
+ /*
+ * poll_cnt - Poll up to 'max' valid completions
+ *
+ * The function returns the number of valid completions it
+ * managed to drain from the CQ.
+ *
+ * Usage example: In case a CQ is connected to one send-queue
+ * the application may use this function to get
+ * the number of the QP send-completions.
+ *
+ * Return value (n):
+ * n >= 0 : number extracted completions.
+ * n == -1 : operation failed. completion is not extracted.
+ * To extract this completion, ibv_poll_cq() must be used
+ *
+ * Note: The function designed to support TX completion, it may also be
+ * used for RX completion but it is not supporting RX inline-scatter.
+ */
+ int32_t (*poll_cnt)(struct ibv_cq *cq, uint32_t max);
+ /*
+ * poll_length - Poll one receive completion and provide the related
+ * message length.
+ *
+ * The function returns only the length of the completed message.
+ * In case of inline received message the message will be copied
+ * to the provided buffer ('buf') and the '*inl' status will be set.
+ * The function extracts only completion of regular receive-messages.
+ * In case of send-message completion or SRQ receive-message completion
+ * it returns -1.
+ *
+ * Usage example: In case a CQ is connected to one receive-queue
+ * the application may use this function to get
+ * the size of the next received message.
+ *
+ * Return value (n):
+ * n > 0 : successful completion with positive length.
+ * *inl will be set to 1 if data was copied to buffer.
+ *
+ * 0 : Empty.
+ * n == -1 : operation failed. completion is not extracted.
+ * To extract this completion, ibv_poll_cq() must be used
+ */
+ int32_t (*poll_length)(struct ibv_cq *cq, void *buf, uint32_t *inl);
+ /*
+ * poll_length_flags - Poll one receive completion and provide the related
+ * message length and completion flags.
+ *
+ * The same as poll_length but also retrieves completion flags as
+ * defined by the enum ibv_exp_cq_family_flags
+ */
+ int32_t (*poll_length_flags)(struct ibv_cq *cq, void *buf, uint32_t *inl, uint32_t *flags);
+ /*
+ * poll_length_flags_mp_rq - Poll one receive completion and provide the related
+ * message length, packet-offset and completion flags.
+ *
+ * The same as poll_length_flags but:
+ * - Without the inline-receive support.
+ * - Also retrieves offset in the WR posted buffer as defined by the WR SG list.
+ * The start of the received packet is located in this offset.
+ */
+ int32_t (*poll_length_flags_mp_rq)(struct ibv_cq *cq, uint32_t *offset, uint32_t *flags);
+ /*
+ * poll_length_flags_cvlan - Poll one receive completion and provide the
+ * related message length, completion flags
+ * and CVLAN TCI.
+ * The CVLAN TCI value is valid only when
+ * IBV_EXP_CQ_RX_CVLAN_STRIPPED_V1 flag is
+ * set.
+ *
+ * The same as poll_length_flags but:
+ * - Also retrievs the packet's CVLAN TCI that was stripped by the HW.
+ */
+ int32_t (*poll_length_flags_cvlan)(struct ibv_cq *cq, void *buf,
+ uint32_t *inl, uint32_t *flags,
+ uint16_t *vlan_tci);
+ /*
+ * poll_length_flags_mp_rq_cvlan - Poll one receive completion and provide
+ * the related message length,
+ * packet-offset, completion flags and
+ * CVLAN TCI
+ *
+ * The same as poll_length_flags_cvlan but:
+ * - Without the inline-receive support.
+ * - Also retrives offset in the WR posted buffer as defined by the
+ * WR SG list. The start of the received packet is located in this
+ * offset.
+ */
+ int32_t (*poll_length_flags_mp_rq_cvlan)(struct ibv_cq *cq,
+ uint32_t *offset,
+ uint32_t *flags,
+ uint16_t *vlan_tci);
+};
+
+enum {
+ IBV_EXP_NUM_DC_INFO_LIDS = 30
+};
+
+struct ibv_exp_dc_info_ent {
+ uint16_t lid[IBV_EXP_NUM_DC_INFO_LIDS];
+ uint32_t seqnum;
+};
+
+enum ibv_exp_roce_gid_type {
+ IBV_EXP_IB_ROCE_V1_GID_TYPE,
+ IBV_EXP_ROCE_V2_GID_TYPE,
+ IBV_EXP_ROCE_V1_5_GID_TYPE,
+};
+
+enum ibv_exp_query_gid_attr {
+ IBV_EXP_QUERY_GID_ATTR_TYPE = (1 << 0),
+ IBV_EXP_QUERY_GID_ATTR_GID = (1 << 1),
+ IBV_EXP_QUERY_GID_ATTR_RESERVED = (1 << 2),
+};
+
+struct ibv_exp_gid_attr {
+ uint32_t comp_mask;
+ enum ibv_exp_roce_gid_type type;
+ union ibv_gid gid;
+};
+
+/**
+ * enum ibv_exp_ec_calc_attr_comp_mask - erasure coding context
+ * init attributes compatibility enumeration
+ */
+enum ibv_exp_ec_calc_attr_comp_mask {
+ IBV_EXP_EC_CALC_ATTR_MAX_INFLIGHT = (1 << 0),
+ IBV_EXP_EC_CALC_ATTR_K = (1 << 1),
+ IBV_EXP_EC_CALC_ATTR_M = (1 << 2),
+ IBV_EXP_EC_CALC_ATTR_W = (1 << 3),
+ IBV_EXP_EC_CALC_ATTR_MAX_DATA_SGE = (1 << 4),
+ IBV_EXP_EC_CALC_ATTR_MAX_CODE_SGE = (1 << 5),
+ IBV_EXP_EC_CALC_ATTR_ENCODE_MAT = (1 << 6),
+ IBV_EXP_EC_CALC_ATTR_AFFINITY = (1 << 7),
+ IBV_EXP_EC_CALC_ATTR_POLLING = (1 << 8),
+ IBV_EXP_EC_CALC_INIT_ATTR_RESERVED = (1 << 9),
+};
+
+/**
+ * struct ibv_exp_ec_calc_init_attr - erasure coding engine
+ * initialization attributes
+ *
+ * @comp_mask: compatibility bitmask
+ * @max_inflight_calcs: maximum inflight calculations
+ * @k: number of data blocks
+ * @m: number of core blocks
+ * @w: Galois field bits GF(2^w)
+ * @max_data_sge: maximum data sg elements to be used for encode/decode
+ * @max_code_sge: maximum code sg elements to be used for encode/decode
+ * @encode_matrix: buffer that contain the encoding matrix
+ * @affinity_hint: affinity hint for asynchronous calcs completion
+ * steering.
+ * @polling: polling mode (if set no completions will be generated
+ * by events).
+ */
+struct ibv_exp_ec_calc_init_attr {
+ uint32_t comp_mask;
+ uint32_t max_inflight_calcs;
+ int k;
+ int m;
+ int w;
+ int max_data_sge;
+ int max_code_sge;
+ uint8_t *encode_matrix;
+ int affinity_hint;
+ int polling;
+};
+
+/**
+ * enum ibv_exp_ec_status - EX calculation status
+ *
+ * @IBV_EXP_EC_CALC_SUCCESS: EC calc operation succeded
+ * @IBV_EXP_EC_CALC_FAIL: EC calc operation failed
+ */
+enum ibv_exp_ec_status {
+ IBV_EXP_EC_CALC_SUCCESS,
+ IBV_EXP_EC_CALC_FAIL,
+};
+
+/**
+ * struct ibv_exp_ec_comp - completion context of EC calculation
+ *
+ * @done: function handle of the EC calculation completion
+ * @status: status of the EC calculation
+ *
+ * The consumer is expected to embed this structure in his calculation context
+ * so that the user context would be acquired back using offsetof()
+ */
+struct ibv_exp_ec_comp {
+ void (*done)(struct ibv_exp_ec_comp *comp);
+ enum ibv_exp_ec_status status;
+};
+
+/**
+ * struct ibv_exp_ec_calc - erasure coding engine context
+ *
+ * @pd: protection domain
+ */
+struct ibv_exp_ec_calc {
+ struct ibv_pd *pd;
+};
+
+/**
+ * struct ibv_exp_ec_mem - erasure coding memory layout context
+ *
+ * @data_blocks: array of data sg elements
+ * @num_data_sge: number of data sg elements
+ * @code_blocks: array of code sg elements
+ * @num_code_sge: number of code sg elements
+ * @block_size: logical block size
+ */
+struct ibv_exp_ec_mem {
+ struct ibv_sge *data_blocks;
+ int num_data_sge;
+ struct ibv_sge *code_blocks;
+ int num_code_sge;
+ int block_size;
+};
+
+/**
+ * struct ibv_exp_ec_stripe - erasure coding stripe descriptor
+ *
+ * @qp: queue-pair connected to the relevant peer
+ * @wr: send work request, can either be a RDMA wr or a SEND
+ */
+struct ibv_exp_ec_stripe {
+ struct ibv_qp *qp;
+ struct ibv_send_wr *wr;
+};
+
+struct ibv_exp_peer_commit;
+struct ibv_exp_rollback_ctx;
+
+
+struct ibv_exp_peer_peek;
+struct ibv_exp_peer_abort_peek;
+
+struct verbs_context_exp {
+ /* "grows up" - new fields go here */
+ int (*exp_peer_peek_cq)(struct ibv_cq *ibcq,
+ struct ibv_exp_peer_peek *peek_ctx);
+ int (*exp_peer_abort_peek_cq)(struct ibv_cq *ibcq,
+ struct ibv_exp_peer_abort_peek *ack_ctx);
+ int (*exp_peer_commit_qp)(struct ibv_qp *qp,
+ struct ibv_exp_peer_commit *peer);
+ int (*exp_rollback_send)(struct ibv_qp *qp,
+ struct ibv_exp_rollback_ctx *rollback);
+ int (*ec_update_sync)(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ uint8_t *data_updates,
+ uint8_t *code_updates);
+ int (*ec_update_async)(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ uint8_t *data_updates,
+ uint8_t *code_updates,
+ struct ibv_exp_ec_comp *ec_comp);
+ struct ibv_exp_ec_calc *(*alloc_ec_calc)(struct ibv_pd *pd,
+ struct ibv_exp_ec_calc_init_attr *attr);
+ void (*dealloc_ec_calc)(struct ibv_exp_ec_calc *calc);
+ int (*ec_encode_async)(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ struct ibv_exp_ec_comp *ec_comp);
+ int (*ec_encode_sync)(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem);
+ int (*ec_decode_async)(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ uint8_t *erasures,
+ uint8_t *decode_matrix,
+ struct ibv_exp_ec_comp *ec_comp);
+ int (*ec_decode_sync)(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ uint8_t *erasures,
+ uint8_t *decode_matrix);
+ int (*ec_poll)(struct ibv_exp_ec_calc *calc, int n);
+ int (*ec_encode_send)(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ struct ibv_exp_ec_stripe *data_stripes,
+ struct ibv_exp_ec_stripe *code_stripes);
+ int (*exp_query_gid_attr)(struct ibv_context *context, uint8_t port_num,
+ unsigned int index,
+ struct ibv_exp_gid_attr *attr);
+ int (*exp_destroy_rwq_ind_table)(struct ibv_exp_rwq_ind_table *rwq_ind_table);
+ struct ibv_exp_rwq_ind_table *(*exp_create_rwq_ind_table)(struct ibv_context *context,
+ struct ibv_exp_rwq_ind_table_init_attr *init_attr);
+ int (*exp_destroy_wq)(struct ibv_exp_wq *wq);
+ int (*exp_modify_wq)(struct ibv_exp_wq *wq,
+ struct ibv_exp_wq_attr *wq_attr);
+ struct ibv_exp_wq * (*exp_create_wq)(struct ibv_context *context,
+ struct ibv_exp_wq_init_attr *wq_init_attr);
+ int (*drv_exp_poll_dc_info)(struct ibv_context *context,
+ struct ibv_exp_dc_info_ent *ents,
+ int nent, int port);
+ void *(*exp_query_intf)(struct ibv_context *context, struct ibv_exp_query_intf_params *params,
+ enum ibv_exp_query_intf_status *status);
+ int (*exp_release_intf)(struct ibv_context *context, void *intf,
+ struct ibv_exp_release_intf_params *params);
+ struct ibv_exp_res_domain *(*exp_create_res_domain)(struct ibv_context *context,
+ struct ibv_exp_res_domain_init_attr *attr);
+ int (*exp_destroy_res_domain)(struct ibv_context *context,
+ struct ibv_exp_res_domain *res_dom,
+ struct ibv_exp_destroy_res_domain_attr *attr);
+ int (*lib_exp_use_priv_env)(struct ibv_context *context);
+ int (*lib_exp_setenv)(struct ibv_context *context, const char *name,
+ const char *value, int overwrite);
+ struct verbs_environment *venv;
+ int (*drv_exp_dereg_mr)(struct ibv_mr *mr, struct ibv_exp_dereg_out *out);
+ int (*exp_rereg_mr)(struct ibv_mr *mr, int flags, struct ibv_pd *pd,
+ void *addr, size_t length, uint64_t access,
+ struct ibv_exp_rereg_mr_attr *attr);
+ int (*drv_exp_rereg_mr)(struct ibv_mr *mr, int flags, struct ibv_pd *pd,
+ void *addr, size_t length, uint64_t access,
+ struct ibv_exp_rereg_mr_attr *attr, struct ibv_exp_rereg_out *out);
+ int (*drv_exp_prefetch_mr)(struct ibv_mr *mr,
+ struct ibv_exp_prefetch_attr *attr);
+ int (*lib_exp_prefetch_mr)(struct ibv_mr *mr,
+ struct ibv_exp_prefetch_attr *attr);
+ struct ibv_exp_mkey_list_container * (*drv_exp_alloc_mkey_list_memory)(struct ibv_exp_mkey_list_container_attr *attr);
+ struct ibv_exp_mkey_list_container * (*lib_exp_alloc_mkey_list_memory)(struct ibv_exp_mkey_list_container_attr *attr);
+ int (*drv_exp_dealloc_mkey_list_memory)(struct ibv_exp_mkey_list_container *mem);
+ int (*lib_exp_dealloc_mkey_list_memory)(struct ibv_exp_mkey_list_container *mem);
+ int (*drv_exp_query_mkey)(struct ibv_mr *mr, struct ibv_exp_mkey_attr *mkey_attr);
+ int (*lib_exp_query_mkey)(struct ibv_mr *mr, struct ibv_exp_mkey_attr *mkey_attr);
+ struct ibv_mr * (*drv_exp_create_mr)(struct ibv_exp_create_mr_in *in);
+ struct ibv_mr * (*lib_exp_create_mr)(struct ibv_exp_create_mr_in *in);
+ int (*drv_exp_arm_dct)(struct ibv_exp_dct *dct, struct ibv_exp_arm_attr *attr);
+ int (*lib_exp_arm_dct)(struct ibv_exp_dct *dct, struct ibv_exp_arm_attr *attr);
+ int (*drv_exp_bind_mw)(struct ibv_exp_mw_bind *mw_bind);
+ int (*lib_exp_bind_mw)(struct ibv_exp_mw_bind *mw_bind);
+ int (*drv_exp_post_send)(struct ibv_qp *qp,
+ struct ibv_exp_send_wr *wr,
+ struct ibv_exp_send_wr **bad_wr);
+ struct ibv_mr * (*drv_exp_reg_mr)(struct ibv_exp_reg_mr_in *in);
+ struct ibv_mr * (*lib_exp_reg_mr)(struct ibv_exp_reg_mr_in *in);
+ struct ibv_ah * (*drv_exp_ibv_create_ah)(struct ibv_pd *pd,
+ struct ibv_exp_ah_attr *attr_exp);
+ int (*drv_exp_query_values)(struct ibv_context *context, int q_values,
+ struct ibv_exp_values *values);
+ struct ibv_cq * (*exp_create_cq)(struct ibv_context *context, int cqe,
+ struct ibv_comp_channel *channel,
+ int comp_vector, struct ibv_exp_cq_init_attr *attr);
+ int (*drv_exp_ibv_poll_cq)(struct ibv_cq *ibcq, int num_entries,
+ struct ibv_exp_wc *wc, uint32_t wc_size);
+ void * (*drv_exp_get_legacy_xrc) (struct ibv_srq *ibv_srq);
+ void (*drv_exp_set_legacy_xrc) (struct ibv_srq *ibv_srq, void *legacy_xrc);
+ struct ibv_mr * (*drv_exp_ibv_reg_shared_mr)(struct ibv_exp_reg_shared_mr_in *in);
+ struct ibv_mr * (*lib_exp_ibv_reg_shared_mr)(struct ibv_exp_reg_shared_mr_in *in);
+ int (*drv_exp_modify_qp)(struct ibv_qp *qp, struct ibv_exp_qp_attr *attr,
+ uint64_t exp_attr_mask);
+ int (*lib_exp_modify_qp)(struct ibv_qp *qp, struct ibv_exp_qp_attr *attr,
+ uint64_t exp_attr_mask);
+ int (*drv_exp_post_task)(struct ibv_context *context,
+ struct ibv_exp_task *task,
+ struct ibv_exp_task **bad_task);
+ int (*lib_exp_post_task)(struct ibv_context *context,
+ struct ibv_exp_task *task,
+ struct ibv_exp_task **bad_task);
+ int (*drv_exp_modify_cq)(struct ibv_cq *cq,
+ struct ibv_exp_cq_attr *attr, int attr_mask);
+ int (*lib_exp_modify_cq)(struct ibv_cq *cq,
+ struct ibv_exp_cq_attr *attr, int attr_mask);
+ int (*drv_exp_ibv_destroy_flow) (struct ibv_exp_flow *flow);
+ int (*lib_exp_ibv_destroy_flow) (struct ibv_exp_flow *flow);
+ struct ibv_exp_flow * (*drv_exp_ibv_create_flow) (struct ibv_qp *qp,
+ struct ibv_exp_flow_attr
+ *flow_attr);
+ struct ibv_exp_flow * (*lib_exp_ibv_create_flow) (struct ibv_qp *qp,
+ struct ibv_exp_flow_attr
+ *flow_attr);
+
+ int (*drv_exp_query_port)(struct ibv_context *context, uint8_t port_num,
+ struct ibv_exp_port_attr *port_attr);
+ int (*lib_exp_query_port)(struct ibv_context *context, uint8_t port_num,
+ struct ibv_exp_port_attr *port_attr);
+ struct ibv_exp_dct *(*create_dct)(struct ibv_context *context,
+ struct ibv_exp_dct_init_attr *attr);
+ int (*destroy_dct)(struct ibv_exp_dct *dct);
+ int (*query_dct)(struct ibv_exp_dct *dct, struct ibv_exp_dct_attr *attr);
+ int (*drv_exp_query_device)(struct ibv_context *context,
+ struct ibv_exp_device_attr *attr);
+ int (*lib_exp_query_device)(struct ibv_context *context,
+ struct ibv_exp_device_attr *attr);
+ struct ibv_qp *(*drv_exp_create_qp)(struct ibv_context *context,
+ struct ibv_exp_qp_init_attr *init_attr);
+ struct ibv_qp *(*lib_exp_create_qp)(struct ibv_context *context,
+ struct ibv_exp_qp_init_attr *init_attr);
+ size_t sz; /* Set by library on struct allocation, */
+ /* must be located as last field */
+};
+
+
+static inline struct verbs_context_exp *verbs_get_exp_ctx(struct ibv_context *ctx)
+{
+ struct verbs_context *app_ex_ctx = verbs_get_ctx(ctx);
+ char *actual_ex_ctx;
+
+ if (!app_ex_ctx || !(app_ex_ctx->has_comp_mask & VERBS_CONTEXT_EXP))
+ return NULL;
+
+ actual_ex_ctx = ((char *)ctx) - (app_ex_ctx->sz - sizeof(struct ibv_context));
+
+ return (struct verbs_context_exp *)(actual_ex_ctx - sizeof(struct verbs_context_exp));
+}
+
+#define verbs_get_exp_ctx_op(ctx, op) ({ \
+ struct verbs_context_exp *_vctx = verbs_get_exp_ctx(ctx); \
+ (!_vctx || (_vctx->sz < sizeof(*_vctx) - offsetof(struct verbs_context_exp, op)) || \
+ !_vctx->op) ? NULL : _vctx; })
+
+#define verbs_set_exp_ctx_op(_vctx, op, ptr) ({ \
+ struct verbs_context_exp *vctx = _vctx; \
+ if (vctx && (vctx->sz >= sizeof(*vctx) - offsetof(struct verbs_context_exp, op))) \
+ vctx->op = ptr; })
+
+
+/*
+ * ibv_exp_alloc_ec_calc() - allocate an erasure coding
+ * calculation offload context
+ * @pd: user allocated protection domain
+ * @attrs: initialization attributes
+ *
+ * Returns handle handle to the EC calculation APIs
+ */
+static inline struct ibv_exp_ec_calc *
+ibv_exp_alloc_ec_calc(struct ibv_pd *pd,
+ struct ibv_exp_ec_calc_init_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(pd->context, alloc_ec_calc);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_EC_CALC_INIT_ATTR_RESERVED - 1);
+
+ return vctx->alloc_ec_calc(pd, attr);
+}
+
+/*
+ * ibv_exp_dealloc_ec_calc() - free an erasure coding
+ * calculation offload context
+ * @ec_calc: ec context
+ */
+static inline void ibv_exp_dealloc_ec_calc(struct ibv_exp_ec_calc *calc)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(calc->pd->context, dealloc_ec_calc);
+ if (!vctx) {
+ errno = ENOSYS;
+ return;
+ }
+
+ vctx->dealloc_ec_calc(calc);
+}
+
+/**
+ * ibv_exp_ec_encode_async() - asynchronous encode of given data blocks
+ * and place in code_blocks
+ * @ec_calc: erasure coding calculation engine
+ * @ec_mem: erasure coding memory layout
+ * @ec_comp: EC calculation completion context
+ *
+ * Restrictions:
+ * - ec_calc is an initialized erasure coding calc engine structure
+ * - ec_mem.data_blocks sg array must describe the data memory
+ * layout, the total length of the sg elements must satisfy
+ * k * ec_mem.block_size.
+ * - ec_mem.num_data_sg must not exceed the calc max_data_sge
+ * - ec_mem.code_blocks sg array must describe the code memory
+ * layout, the total length of the sg elements must satisfy
+ * m * ec_mem.block_size.
+ * - ec_mem.num_code_sg must not exceed the calc max_code_sge
+ *
+ * Notes:
+ * The ec_calc will perform the erasure coding calc operation,
+ * once it completes, it will call ec_comp->done() handle.
+ * The caller will take it from there.
+ */
+static inline int
+ibv_exp_ec_encode_async(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ struct ibv_exp_ec_comp *ec_comp)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(calc->pd->context, ec_encode_async);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->ec_encode_async(calc, ec_mem, ec_comp);
+}
+
+/**
+ * ibv_exp_ec_encode_sync() - synchronous encode of given data blocks
+ * and place in code_blocks
+ * @ec_calc: erasure coding calculation engine
+ * @ec_mem: erasure coding memory layout
+ *
+ * Restrictions:
+ * - ec_calc is an initialized erasure coding calc engine structure
+ * - ec_mem.data_blocks sg array must describe the data memory
+ * layout, the total length of the sg elements must satisfy
+ * k * ec_mem.block_size.
+ * - ec_mem.num_data_sg must not exceed the calc max_data_sge
+ * - ec_mem.code_blocks sg array must describe the code memory
+ * layout, the total length of the sg elements must satisfy
+ * m * ec_mem.block_size.
+ * - ec_mem.num_code_sg must not exceed the calc max_code_sge
+ *
+ * Returns 0 on success, non-zero on failure.
+ *
+ */
+static inline int
+ibv_exp_ec_encode_sync(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(calc->pd->context, ec_encode_sync);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->ec_encode_sync(calc, ec_mem);
+}
+
+/**
+ * ibv_exp_ec_decode_async() - decode a given set of data blocks
+ * and code_blocks and place into output recovery blocks
+ * @ec_calc: erasure coding calculation engine
+ * @ec_mem: erasure coding memory layout
+ * @erasures: pointer to byte-map of which blocks were erased
+ * and needs to be recovered
+ * @decode_matrix: buffer that contains the decode matrix
+ * @ec_comp: EC calculation completion context
+ *
+ * Restrictions:
+ * - ec_calc is an initialized erasure coding calc engine structure
+ * - ec_mem.data_blocks sg array must describe the data memory
+ * layout, the total length of the sg elements must satisfy
+ * k * ec_mem.block_size.
+ * - ec_mem.num_data_sg must not exceed the calc max_data_sge
+ * - ec_mem.code_blocks sg array must describe the code memory
+ * layout, the total length of the sg elements must satisfy
+ * number of missing blocks * ec_mem.block_size.
+ * - ec_mem.num_code_sg must not exceed the calc max_code_sge
+ * - erasures byte-mask consists of the survived and erased blocks.
+ * The first k bytes stand for the k data blocks followed by
+ * m bytes that stand for the code blocks.
+ *
+ * Returns 0 on success, or non-zero on failure with a corresponding
+ * errno.
+ *
+ * Notes:
+ * The ec_calc will perform the erasure coding calc operation,
+ * once it completes, it will call ec_comp->done() handle.
+ * The caller will take it from there
+ */
+static inline int
+ibv_exp_ec_decode_async(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ uint8_t *erasures,
+ uint8_t *decode_matrix,
+ struct ibv_exp_ec_comp *ec_comp)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(calc->pd->context, ec_decode_async);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->ec_decode_async(calc, ec_mem, erasures,
+ decode_matrix, ec_comp);
+}
+
+/**
+ * ibv_exp_ec_decode_sync() - decode a given set of data blocks
+ * and code_blocks and place into output recovery blocks
+ * @ec_calc: erasure coding calculation engine
+ * @ec_mem: erasure coding memory layout
+ * @erasures: pointer to byte-map of which blocks were erased
+ * and needs to be recovered
+ * @decode_matrix: registered buffer of the decode matrix
+ *
+ * Restrictions:
+ * - ec_calc is an initialized erasure coding calc engine structure
+ * - ec_mem.data_blocks sg array must describe the data memory
+ * layout, the total length of the sg elements must satisfy
+ * k * ec_mem.block_size.
+ * - ec_mem.num_data_sg must not exceed the calc max_data_sge
+ * - ec_mem.code_blocks sg array must describe the code memory
+ * layout, the total length of the sg elements must satisfy
+ * number of missing blocks * ec_mem.block_size.
+ * - ec_mem.num_code_sg must not exceed the calc max_code_sge
+ * - erasures byte-map consists of the survived and erased blocks.
+ * The first k bytes stand for the k data blocks followed by
+ * m bytes that stand for the code blocks.
+ *
+ * Returns 0 on success, or non-zero on failure with a corresponding
+ * errno.
+ */
+static inline int
+ibv_exp_ec_decode_sync(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ uint8_t *erasures,
+ uint8_t *decode_matrix)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(calc->pd->context, ec_decode_sync);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->ec_decode_sync(calc, ec_mem, erasures, decode_matrix);
+}
+
+/**
+ * ibv_exp_ec_update_async() - copmutes redundancies based on updated blocks,
+ * their replacements and old redundancies and place into output code blocks
+ * @ec_calc: erasure coding calculation engine
+ * @ec_mem: erasure coding memory layout
+ * @data_updates: array which is a map of data blocks that are updated
+ * @code_updates: array which is a map of code blocks to be computed
+ * @ec_comp: EC calculation completion context
+ *
+ * Restrictions:
+ * - ec_calc is an initialized erasure coding calc engine structure
+ * - ec_mem.data_blocks sg array must describe the data memory
+ * layout in the following way:
+ * assume we want to update blocks d_i and d_j with i<j,
+ * then sg enries should be as follows:
+ * c_0 ... c_m d_i d'_i d_j d'_j
+ * were c_0 ... c_m are all previous redundancies,
+ * d_i is an original i-th block, d'_i is new i-th block
+ * - ec_mem.num_data_sg should be equal to the number of sg entries,
+ * i.e. to num of code blocks to be updated + 2*num of updates
+ * - ec_mem.code_blocks sg array must describe the code memory
+ * layout, the total length of the sg elements must satisfy
+ * number of overall code blocks to be updated.
+ * - ec_mem.num_code_sg must be equal to the number of code blocks
+ * to be updated and not to exceed the calc max_code_sge
+ * - data_updates is an array of size k (=number of data blocks)
+ * and is a byte-map for blocks to be updated, i.e
+ * if we want to update i-th block and do not want to update j-th block,
+ * then data_updates[i]=1 and data_updates[j]=0.
+ * - code_updates is an array of size m(=number of code blocks)
+ * and is a byte-map of code blocks that should be computed, i.e
+ * if we want to compute i-th block and do not want to compute j-th block,
+ * then code_updates[i]=1 and code_updates[j]=0.
+ *
+ * Returns 0 on success, or non-zero on failure with a corresponding
+ * errno.
+ */
+
+static inline int
+ibv_exp_ec_update_async(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ uint8_t *data_updates,
+ uint8_t *code_updates,
+ struct ibv_exp_ec_comp *ec_comp)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(calc->pd->context, ec_update_async);
+ if (!vctx)
+ return -ENOSYS;
+
+ return vctx->ec_update_async(calc, ec_mem, data_updates,
+ code_updates, ec_comp);
+}
+
+/**
+ * ibv_exp_ec_update_sync() - copmutes redundancies based on updated blocks,
+ * their replacements and old redundancies and place into output code blocks
+ * @ec_calc: erasure coding calculation engine
+ * @ec_mem: erasure coding memory layout
+ * @data_updates: array which is a map of data blocks that are updated
+ * @code_updates: array which is a map of code blocks to be computed
+ *
+ * Restrictions:
+ * - ec_calc is an initialized erasure coding calc engine structure
+ * - ec_mem.data_blocks sg array must describe the data memory
+ * layout in the following way:
+ * assume we want to update blocks d_i and d_j with i<j,
+ * then enries of sg should be as follows:
+ * c_0..c_m d_i d'_i d_j d'_j
+ * were c_0 .. c_m are previous redundancies,
+ * d_i is an original i-th block, d'_i is new i-th block
+ * - ec_mem.num_data_sg should be equal to the number of sg entries,
+ * i.e. to num of code blocks to be updated + 2*num of updates
+ * - ec_mem.code_blocks sg array must describe the code memory
+ * layout, the total length of the sg elements must satisfy
+ * number of overall code blocks to be updated.
+ * - ec_mem.num_code_sg must be equal to the number of code blocks
+ * to be updated and not to exceed the calc max_code_sge
+ * - data_updates is an array of size k (=number of data blocks)
+ * and is a byte-map for blocks to be updated, i.e
+ * if we want to update i-th block and do not want to update j-th block,
+ * then data_updates[i]=1 and data_updates[j]=0.
+ * - code_updates is an array of size m(=number of code blocks)
+ * and is a bytemap of code blocks that should be computed, i.e
+ * if we want to compute i-th block and do not want to compute j-th block,
+ * then code_updates[i]=1 and code_updates[j]=0.
+ *
+ * Returns 0 on success, or non-zero on failure with a corresponding
+ * errno.
+ */
+
+static inline int
+ibv_exp_ec_update_sync(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ uint8_t *data_updates,
+ uint8_t *code_updates)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(calc->pd->context, ec_update_sync);
+ if (!vctx)
+ return -ENOSYS;
+
+ return vctx->ec_update_sync(calc, ec_mem, data_updates, code_updates);
+}
+/**
+ * ibv_exp_ec_calc_poll() - poll for EC calculation
+ *
+ * @calc: EC calc context
+ * @n: number of calculations to poll
+ *
+ * Returns the number of calc completions processed which
+ * is lower or equal to n. Relevant only when EC calc context
+ * was allocated in polling mode.
+ */
+static inline int
+ibv_exp_ec_poll(struct ibv_exp_ec_calc *calc, int n)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(calc->pd->context, ec_poll);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->ec_poll(calc, n);
+}
+
+/**
+ * ibv_exp_ec_encode_send() - encode a given data blocks
+ * initiate the data and code blocks transfers to the wire with the qps array.
+ * @ec_calc: erasure coding calculation engine
+ * @ec_mem: erasure coding memory layout context
+ * @data_stripes: array of stripe handles, each represents a data block channel
+ * @code_stripes: array of qp handles, each represents a code block channel
+ *
+ * Restrictions:
+ * - ec_calc is an initialized erasure coding calc engine structure
+ * - ec_mem.data_blocks sg array must describe the data memory
+ * layout, the total length of the sg elements must satisfy
+ * k * ec_mem.block_size.
+ * - ec_mem.num_data_sg must not exceed the calc max_data_sge
+ * - ec_mem.code_blocks sg array must describe the code memory
+ * layout, the total length of the sg elements must satisfy
+ * m * ec_mem.block_size.
+ * - ec_mem.num_code_sg must not exceed the calc max_code_sge
+ *
+ * Returns 0 on success, or non-zero on failure with a corresponding
+ * errno.
+ */
+static inline int
+ibv_exp_ec_encode_send(struct ibv_exp_ec_calc *calc,
+ struct ibv_exp_ec_mem *ec_mem,
+ struct ibv_exp_ec_stripe *data_stripes,
+ struct ibv_exp_ec_stripe *code_stripes)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(calc->pd->context, ec_encode_send);
+ if (!vctx)
+ return -ENOSYS;
+
+ return vctx->ec_encode_send(calc, ec_mem, data_stripes, code_stripes);
+}
+
+static inline struct ibv_qp *
+ibv_exp_create_qp(struct ibv_context *context, struct ibv_exp_qp_init_attr *qp_init_attr)
+{
+ struct verbs_context_exp *vctx;
+ uint32_t mask = qp_init_attr->comp_mask;
+
+ if (mask == IBV_EXP_QP_INIT_ATTR_PD)
+ return ibv_create_qp(qp_init_attr->pd,
+ (struct ibv_qp_init_attr *) qp_init_attr);
+
+ vctx = verbs_get_exp_ctx_op(context, lib_exp_create_qp);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(qp_init_attr->comp_mask,
+ IBV_EXP_QP_INIT_ATTR_RESERVED1 - 1);
+
+ return vctx->lib_exp_create_qp(context, qp_init_attr);
+}
+
+/*
+ * ibv_exp_use_priv_env
+ *
+ * switch to use private environment
+ */
+static inline int ibv_exp_use_priv_env(struct ibv_context *context)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, lib_exp_use_priv_env);
+ if (!vctx) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return vctx->lib_exp_use_priv_env(context);
+}
+
+/*
+ * ibv_exp_poll_dc_info
+ *
+ * The function is not thread safe. Any locking must be done by the user.
+ *
+ * Return: >= 0 number of returned entries
+ * < 0 error
+ *
+ */
+static inline int ibv_exp_poll_dc_info(struct ibv_context *context,
+ struct ibv_exp_dc_info_ent *ents,
+ int nent, int port)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, drv_exp_poll_dc_info);
+ if (!vctx) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return vctx->drv_exp_poll_dc_info(context, ents, nent, port);
+}
+
+/*
+ * ibv_exp_setenv
+ *
+ * see man setenv for parameter description
+ */
+static inline int ibv_exp_setenv(struct ibv_context *context,
+ const char *name,
+ const char *value,
+ int overwrite)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, lib_exp_setenv);
+ if (!vctx)
+ return setenv(name, value, overwrite);
+
+ return vctx->lib_exp_setenv(context, name, value, overwrite);
+}
+
+static inline int ibv_exp_query_device(struct ibv_context *context,
+ struct ibv_exp_device_attr *attr)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(context,
+ lib_exp_query_device);
+ if (!vctx)
+ return ENOSYS;
+
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_DEVICE_ATTR_RESERVED - 1);
+ return vctx->lib_exp_query_device(context, attr);
+}
+
+static inline struct ibv_exp_dct *
+ibv_exp_create_dct(struct ibv_context *context,
+ struct ibv_exp_dct_init_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+ struct ibv_exp_dct *dct;
+
+ vctx = verbs_get_exp_ctx_op(context, create_dct);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_DCT_INIT_ATTR_RESERVED - 1);
+ pthread_mutex_lock(&context->mutex);
+ dct = vctx->create_dct(context, attr);
+ if (dct)
+ dct->context = context;
+
+ pthread_mutex_unlock(&context->mutex);
+
+ return dct;
+}
+
+static inline int ibv_exp_destroy_dct(struct ibv_exp_dct *dct)
+{
+ struct verbs_context_exp *vctx;
+ struct ibv_context *context = dct->context;
+ int err;
+
+ vctx = verbs_get_exp_ctx_op(context, destroy_dct);
+ if (!vctx) {
+ errno = ENOSYS;
+ return errno;
+ }
+
+ pthread_mutex_lock(&context->mutex);
+ err = vctx->destroy_dct(dct);
+ pthread_mutex_unlock(&context->mutex);
+
+ return err;
+}
+
+static inline int ibv_exp_query_dct(struct ibv_exp_dct *dct,
+ struct ibv_exp_dct_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+ struct ibv_context *context = dct->context;
+ int err;
+
+ vctx = verbs_get_exp_ctx_op(context, query_dct);
+ if (!vctx) {
+ errno = ENOSYS;
+ return errno;
+ }
+
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_DCT_ATTR_RESERVED - 1);
+ pthread_mutex_lock(&context->mutex);
+ err = vctx->query_dct(dct, attr);
+ pthread_mutex_unlock(&context->mutex);
+
+ return err;
+}
+
+static inline int ibv_exp_arm_dct(struct ibv_exp_dct *dct,
+ struct ibv_exp_arm_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+ struct ibv_context *context = dct->context;
+ int err;
+
+ vctx = verbs_get_exp_ctx_op(context, lib_exp_arm_dct);
+ if (!vctx) {
+ errno = ENOSYS;
+ return errno;
+ }
+
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_ARM_ATTR_RESERVED - 1);
+ pthread_mutex_lock(&context->mutex);
+ err = vctx->lib_exp_arm_dct(dct, attr);
+ pthread_mutex_unlock(&context->mutex);
+
+ return err;
+}
+
+static inline int ibv_exp_query_port(struct ibv_context *context,
+ uint8_t port_num,
+ struct ibv_exp_port_attr *port_attr)
+{
+ struct verbs_context_exp *vctx;
+
+ if (0 == port_attr->comp_mask)
+ return ibv_query_port(context, port_num,
+ &port_attr->port_attr);
+
+ /* Check that only valid flags were given */
+ if ((!port_attr->comp_mask & IBV_EXP_QUERY_PORT_ATTR_MASK1) ||
+ (port_attr->comp_mask & ~IBV_EXP_QUERY_PORT_ATTR_MASKS) ||
+ (port_attr->mask1 & ~IBV_EXP_QUERY_PORT_MASK)) {
+ errno = EINVAL;
+ return -errno;
+ }
+
+ vctx = verbs_get_exp_ctx_op(context, lib_exp_query_port);
+
+ if (!vctx) {
+ /* Fallback to legacy mode */
+ if (port_attr->comp_mask == IBV_EXP_QUERY_PORT_ATTR_MASK1 &&
+ !(port_attr->mask1 & ~IBV_EXP_QUERY_PORT_STD_MASK))
+ return ibv_query_port(context, port_num,
+ &port_attr->port_attr);
+
+ /* Unsupported field was requested */
+ errno = ENOSYS;
+ return -errno;
+ }
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(port_attr->comp_mask,
+ IBV_EXP_QUERY_PORT_ATTR_RESERVED - 1);
+
+ return vctx->lib_exp_query_port(context, port_num, port_attr);
+}
+
+/**
+ * ibv_exp_post_task - Post a list of tasks to different QPs.
+ */
+static inline int ibv_exp_post_task(struct ibv_context *context,
+ struct ibv_exp_task *task,
+ struct ibv_exp_task **bad_task)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(context,
+ lib_exp_post_task);
+ if (!vctx)
+ return ENOSYS;
+
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(task->comp_mask,
+ IBV_EXP_TASK_RESERVED - 1);
+
+ return vctx->lib_exp_post_task(context, task, bad_task);
+}
+
+static inline int ibv_exp_query_values(struct ibv_context *context, int q_values,
+ struct ibv_exp_values *values)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(context,
+ drv_exp_query_values);
+ if (!vctx) {
+ errno = ENOSYS;
+ return -errno;
+ }
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(values->comp_mask,
+ IBV_EXP_VALUES_RESERVED - 1);
+
+ return vctx->drv_exp_query_values(context, q_values, values);
+}
+
+static inline struct ibv_exp_flow *ibv_exp_create_flow(struct ibv_qp *qp,
+ struct ibv_exp_flow_attr *flow)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(qp->context,
+ lib_exp_ibv_create_flow);
+ if (!vctx || !vctx->lib_exp_ibv_create_flow)
+ return NULL;
+
+ if (flow->reserved != 0L) {
+ fprintf(stderr, "%s:%d: flow->reserved must be 0\n", __FUNCTION__, __LINE__);
+ flow->reserved = 0L;
+ }
+
+ return vctx->lib_exp_ibv_create_flow(qp, flow);
+}
+
+static inline int ibv_exp_destroy_flow(struct ibv_exp_flow *flow_id)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(flow_id->context,
+ lib_exp_ibv_destroy_flow);
+ if (!vctx || !vctx->lib_exp_ibv_destroy_flow)
+ return -ENOSYS;
+
+ return vctx->lib_exp_ibv_destroy_flow(flow_id);
+}
+
+static inline int ibv_exp_poll_cq(struct ibv_cq *ibcq, int num_entries,
+ struct ibv_exp_wc *wc, uint32_t wc_size)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(ibcq->context,
+ drv_exp_ibv_poll_cq);
+ if (!vctx)
+ return -ENOSYS;
+
+ return vctx->drv_exp_ibv_poll_cq(ibcq, num_entries, wc, wc_size);
+}
+
+/**
+ * ibv_exp_post_send - Post a list of work requests to a send queue.
+ */
+static inline int ibv_exp_post_send(struct ibv_qp *qp,
+ struct ibv_exp_send_wr *wr,
+ struct ibv_exp_send_wr **bad_wr)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(qp->context,
+ drv_exp_post_send);
+ if (!vctx)
+ return -ENOSYS;
+
+ return vctx->drv_exp_post_send(qp, wr, bad_wr);
+}
+
+/**
+ * ibv_exp_reg_shared_mr - Register to an existing shared memory region
+ * @in - Experimental register shared MR input data.
+ */
+static inline struct ibv_mr *ibv_exp_reg_shared_mr(struct ibv_exp_reg_shared_mr_in *mr_in)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(mr_in->pd->context,
+ lib_exp_ibv_reg_shared_mr);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(mr_in->comp_mask,
+ IBV_EXP_REG_SHARED_MR_RESERVED - 1);
+
+ return vctx->lib_exp_ibv_reg_shared_mr(mr_in);
+}
+
+/**
+ * ibv_exp_modify_cq - Modifies the attributes for the specified CQ.
+ * @cq: The CQ to modify.
+ * @cq_attr: Specifies the CQ attributes to modify.
+ * @cq_attr_mask: A bit-mask used to specify which attributes of the CQ
+ * are being modified.
+ */
+static inline int ibv_exp_modify_cq(struct ibv_cq *cq,
+ struct ibv_exp_cq_attr *cq_attr,
+ int cq_attr_mask)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(cq->context,
+ lib_exp_modify_cq);
+ if (!vctx)
+ return ENOSYS;
+
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(cq_attr->comp_mask,
+ IBV_EXP_CQ_ATTR_RESERVED - 1);
+
+ return vctx->lib_exp_modify_cq(cq, cq_attr, cq_attr_mask);
+}
+
+static inline struct ibv_cq *ibv_exp_create_cq(struct ibv_context *context,
+ int cqe,
+ void *cq_context,
+ struct ibv_comp_channel *channel,
+ int comp_vector,
+ struct ibv_exp_cq_init_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+ struct ibv_cq *cq;
+
+ vctx = verbs_get_exp_ctx_op(context, exp_create_cq);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_CQ_INIT_ATTR_RESERVED1 - 1);
+ pthread_mutex_lock(&context->mutex);
+ cq = vctx->exp_create_cq(context, cqe, channel, comp_vector, attr);
+ if (cq) {
+ cq->context = context;
+ cq->channel = channel;
+ if (channel)
+ ++channel->refcnt;
+ cq->cq_context = cq_context;
+ cq->comp_events_completed = 0;
+ cq->async_events_completed = 0;
+ pthread_mutex_init(&cq->mutex, NULL);
+ pthread_cond_init(&cq->cond, NULL);
+ }
+
+ pthread_mutex_unlock(&context->mutex);
+
+ return cq;
+}
+
+/**
+ * ibv_exp_modify_qp - Modify a queue pair.
+ * The argument exp_attr_mask specifies the QP attributes to be modified.
+ * Use ibv_exp_qp_attr_mask for this argument.
+ */
+static inline int
+ibv_exp_modify_qp(struct ibv_qp *qp, struct ibv_exp_qp_attr *attr, uint64_t exp_attr_mask)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(qp->context, lib_exp_modify_qp);
+ if (!vctx) {
+ errno = ENOSYS;
+ return errno;
+ }
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_QP_ATTR_RESERVED - 1);
+
+ return vctx->lib_exp_modify_qp(qp, attr, exp_attr_mask);
+}
+
+/**
+ * ibv_exp_reg_mr - Register a memory region
+ */
+static inline struct ibv_mr *ibv_exp_reg_mr(struct ibv_exp_reg_mr_in *in)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(in->pd->context, lib_exp_reg_mr);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(in->comp_mask,
+ IBV_EXP_REG_MR_RESERVED - 1);
+
+ return vctx->lib_exp_reg_mr(in);
+}
+
+
+/**
+ * ibv_exp_bind_mw - Bind a memory window to a region
+ */
+static inline int ibv_exp_bind_mw(struct ibv_exp_mw_bind *mw_bind)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(mw_bind->mw->context, lib_exp_bind_mw);
+ if (!vctx) {
+ errno = ENOSYS;
+ return errno;
+ }
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(mw_bind->comp_mask,
+ IBV_EXP_BIND_MW_RESERVED - 1);
+
+ return vctx->lib_exp_bind_mw(mw_bind);
+}
+
+/**
+ * ibv_exp_prefetch_mr - Prefetch part of a memory region.
+ *
+ * Can be used only with MRs registered with IBV_EXP_ACCESS_ON_DEMAND
+ *
+ * Returns 0 on success,
+ * - ENOSYS libibverbs or provider driver doesn't support the prefetching verb.
+ * - EFAULT when the range requested is out of the memory region bounds, or when
+ * parts of it are not part of the process address space.
+ * - EINVAL when the MR is invalid.
+ */
+static inline int ibv_exp_prefetch_mr(
+ struct ibv_mr *mr,
+ struct ibv_exp_prefetch_attr *attr)
+{
+ struct verbs_context_exp *vctx = verbs_get_exp_ctx_op(mr->context,
+ lib_exp_prefetch_mr);
+
+ if (!vctx) {
+ errno = ENOSYS;
+ return errno;
+ }
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_PREFETCH_MR_RESERVED - 1);
+
+ return vctx->lib_exp_prefetch_mr(mr, attr);
+}
+
+typedef int (*drv_exp_post_send_func)(struct ibv_qp *qp,
+ struct ibv_exp_send_wr *wr,
+ struct ibv_exp_send_wr **bad_wr);
+typedef int (*drv_post_send_func)(struct ibv_qp *qp, struct ibv_send_wr *wr,
+ struct ibv_send_wr **bad_wr);
+typedef int (*drv_exp_poll_cq_func)(struct ibv_cq *ibcq, int num_entries,
+ struct ibv_exp_wc *wc, uint32_t wc_size);
+typedef int (*drv_poll_cq_func)(struct ibv_cq *cq, int num_entries, struct ibv_wc *wc);
+typedef int (*drv_post_recv_func)(struct ibv_qp *qp, struct ibv_recv_wr *wr,
+ struct ibv_recv_wr **bad_wr);
+
+static inline void *ibv_exp_get_provider_func(struct ibv_context *context,
+ enum ibv_exp_func_name name)
+{
+ struct verbs_context_exp *vctx;
+
+ switch (name) {
+ case IBV_EXP_POST_SEND_FUNC:
+ vctx = verbs_get_exp_ctx_op(context, drv_exp_post_send);
+ if (!vctx)
+ goto error;
+
+ return (void *)vctx->drv_exp_post_send;
+
+ case IBV_EXP_POLL_CQ_FUNC:
+ vctx = verbs_get_exp_ctx_op(context, drv_exp_ibv_poll_cq);
+ if (!vctx)
+ goto error;
+
+ return (void *)vctx->drv_exp_ibv_poll_cq;
+
+ case IBV_POST_SEND_FUNC:
+ if (!context->ops.post_send)
+ goto error;
+
+ return (void *)context->ops.post_send;
+
+ case IBV_POLL_CQ_FUNC:
+ if (!context->ops.poll_cq)
+ goto error;
+
+ return (void *)context->ops.poll_cq;
+
+ case IBV_POST_RECV_FUNC:
+ if (!context->ops.post_recv)
+ goto error;
+
+ return (void *)context->ops.post_recv;
+
+ default:
+ break;
+ }
+
+error:
+ errno = ENOSYS;
+ return NULL;
+}
+
+static inline struct ibv_mr *ibv_exp_create_mr(struct ibv_exp_create_mr_in *in)
+{
+ struct verbs_context_exp *vctx;
+ struct ibv_mr *mr;
+
+ vctx = verbs_get_exp_ctx_op(in->pd->context, lib_exp_create_mr);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(in->comp_mask,
+ IBV_EXP_CREATE_MR_IN_RESERVED - 1);
+ mr = vctx->lib_exp_create_mr(in);
+ if (mr)
+ mr->pd = in->pd;
+
+ return mr;
+}
+
+static inline int ibv_exp_query_mkey(struct ibv_mr *mr,
+ struct ibv_exp_mkey_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(mr->context, lib_exp_query_mkey);
+ if (!vctx) {
+ errno = ENOSYS;
+ return errno;
+ }
+
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_MKEY_ATTR_RESERVED - 1);
+
+ return vctx->lib_exp_query_mkey(mr, attr);
+}
+
+static inline int ibv_exp_dealloc_mkey_list_memory(struct ibv_exp_mkey_list_container *mem)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(mem->context,
+ lib_exp_dealloc_mkey_list_memory);
+ if (!vctx) {
+ errno = ENOSYS;
+ return errno;
+ }
+
+ return vctx->lib_exp_dealloc_mkey_list_memory(mem);
+}
+
+static inline struct ibv_exp_mkey_list_container *
+ibv_exp_alloc_mkey_list_memory(struct ibv_exp_mkey_list_container_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(attr->pd->context,
+ lib_exp_alloc_mkey_list_memory);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_MKEY_LIST_CONTAINER_RESERVED - 1);
+
+ return vctx->lib_exp_alloc_mkey_list_memory(attr);
+}
+
+/**
+ * ibv_rereg_mr - Re-Register a memory region
+ *
+ * For exp_access use ibv_exp_access_flags
+ */
+static inline int ibv_exp_rereg_mr(struct ibv_mr *mr, int flags,
+ struct ibv_pd *pd, void *addr,
+ size_t length, uint64_t exp_access,
+ struct ibv_exp_rereg_mr_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(mr->context, exp_rereg_mr);
+ if (!vctx)
+ return errno = ENOSYS;
+
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_REREG_MR_ATTR_RESERVED - 1);
+
+ return vctx->exp_rereg_mr(mr, flags, pd, addr, length, exp_access, attr);
+}
+
+/**
+ * ibv_exp_create_res_domain - create resource domain
+ */
+static inline struct ibv_exp_res_domain *ibv_exp_create_res_domain(struct ibv_context *context,
+ struct ibv_exp_res_domain_init_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, exp_create_res_domain);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_RES_DOMAIN_RESERVED - 1);
+
+ return vctx->exp_create_res_domain(context, attr);
+}
+
+/**
+ * ibv_exp_destroy_res_domain - destroy resource domain
+ */
+static inline int ibv_exp_destroy_res_domain(struct ibv_context *context,
+ struct ibv_exp_res_domain *res_dom,
+ struct ibv_exp_destroy_res_domain_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, exp_destroy_res_domain);
+ if (!vctx)
+ return errno = ENOSYS;
+
+ if (attr)
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_DESTROY_RES_DOMAIN_RESERVED - 1);
+
+ return vctx->exp_destroy_res_domain(context, res_dom, attr);
+}
+
+/**
+ * ibv_exp_query_intf - query for family of verbs interface for specific QP/CQ
+ *
+ * Usually family of data-path verbs.
+ * Application may call ibv_exp_query_intf for QPs in the following states:
+ * IBV_QPS_INIT, IBV_QPS_RTR and IBV_QPS_RTS
+ *
+ * Returns the family of verbs.
+ * On failure returns NULL. The failure reason provided by the 'status'
+ * output variable.
+ */
+static inline void *ibv_exp_query_intf(struct ibv_context *context,
+ struct ibv_exp_query_intf_params *params,
+ enum ibv_exp_query_intf_status *status)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, exp_query_intf);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(params->comp_mask,
+ IBV_EXP_QUERY_INTF_RESERVED - 1);
+
+ return vctx->exp_query_intf(context, params, status);
+}
+
+/**
+ * ibv_exp_release_intf - release the queried interface
+ */
+static inline int ibv_exp_release_intf(struct ibv_context *context, void *intf,
+ struct ibv_exp_release_intf_params *params)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, exp_release_intf);
+ if (!vctx)
+ return errno = ENOSYS;
+
+ if (params)
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(params->comp_mask,
+ IBV_EXP_RELEASE_INTF_RESERVED - 1);
+
+ return vctx->exp_release_intf(context, intf, params);
+}
+
+static inline struct ibv_exp_wq *ibv_exp_create_wq(struct ibv_context *context,
+ struct ibv_exp_wq_init_attr *wq_init_attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, exp_create_wq);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(wq_init_attr->comp_mask,
+ IBV_EXP_CREATE_WQ_RESERVED - 1);
+
+ return vctx->exp_create_wq(context, wq_init_attr);
+}
+
+static inline int ibv_exp_modify_wq(struct ibv_exp_wq *wq, struct ibv_exp_wq_attr *wq_attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(wq->context, exp_modify_wq);
+ if (!vctx)
+ return ENOSYS;
+
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(wq_attr->attr_mask,
+ IBV_EXP_WQ_ATTR_RESERVED - 1);
+ return vctx->exp_modify_wq(wq, wq_attr);
+}
+
+static inline int ibv_exp_destroy_wq(struct ibv_exp_wq *wq)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(wq->context, exp_destroy_wq);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->exp_destroy_wq(wq);
+}
+
+/*
+ * ibv_exp_create_rwq_ind_table - Creates a RQ Indirection Table associated
+ * with the specified protection domain.
+ * @pd: The protection domain associated with the Indirection Table.
+ * @ibv_exp_rwq_ind_table_init_attr: A list of initial attributes required to
+ * create the Indirection Table.
+ * Return Value
+ * ibv_exp_create_rwq_ind_table returns a pointer to the created
+ * Indirection Table, or NULL if the request fails.
+ */
+static inline struct ibv_exp_rwq_ind_table *ibv_exp_create_rwq_ind_table(struct ibv_context *context,
+ struct ibv_exp_rwq_ind_table_init_attr *init_attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, exp_create_rwq_ind_table);
+ if (!vctx) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ IBV_EXP_RET_NULL_ON_INVALID_COMP_MASK(init_attr->comp_mask,
+ IBV_EXP_CREATE_IND_TABLE_RESERVED - 1);
+ return vctx->exp_create_rwq_ind_table(context, init_attr);
+}
+
+/*
+ * ibv_exp_destroy_rwq_ind_table - Destroys the specified Indirection Table.
+ * @rwq_ind_table: The Indirection Table to destroy.
+ * Return Value
+ * ibv_destroy_rwq_ind_table() returns 0 on success, or the value of errno
+ * on failure (which indicates the failure reason).
+*/
+static inline int ibv_exp_destroy_rwq_ind_table(struct ibv_exp_rwq_ind_table *rwq_ind_table)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(rwq_ind_table->context, exp_destroy_rwq_ind_table);
+ if (!vctx)
+ return ENOSYS;
+
+ return vctx->exp_destroy_rwq_ind_table(rwq_ind_table);
+}
+
+/*
+ * ibv_exp_query_gid_attr - query a GID attributes
+ * @context: ib context
+ * @port_num: port number
+ * @index: gid index in the gids table
+ * @attr: the gid attributes of index in the gids table
+ * Return value
+ * ibv_exp_query_gid_attr return 0 on success, or the value of errno on failure.
+ */
+static inline int ibv_exp_query_gid_attr(struct ibv_context *context,
+ uint8_t port_num,
+ unsigned int index,
+ struct ibv_exp_gid_attr *attr)
+{
+ struct verbs_context_exp *vctx;
+
+ vctx = verbs_get_exp_ctx_op(context, exp_query_gid_attr);
+ if (!vctx)
+ return ENOSYS;
+
+ IBV_EXP_RET_EINVAL_ON_INVALID_COMP_MASK(attr->comp_mask,
+ IBV_EXP_QUERY_GID_ATTR_RESERVED - 1);
+ return vctx->exp_query_gid_attr(context, port_num, index, attr);
+}
+END_C_DECLS
+
+#define VERBS_MAX_ENV_VAL 4096
+
+# undef __attribute_const
+
+
+#endif /* INFINIBAND_VERBS_EXP_H */
diff --git a/external_libs/ibverbs/libibverbs.a b/external_libs/ibverbs/libibverbs.a
new file mode 100644
index 00000000..e62d4c4e
--- /dev/null
+++ b/external_libs/ibverbs/libibverbs.a
Binary files differ
diff --git a/external_libs/ibverbs/libibverbs.la b/external_libs/ibverbs/libibverbs.la
new file mode 100644
index 00000000..332deb34
--- /dev/null
+++ b/external_libs/ibverbs/libibverbs.la
@@ -0,0 +1,41 @@
+# libibverbs.la - a libtool library file
+# Generated by libtool (GNU libtool) 2.4.2
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='libibverbs.so.1'
+
+# Names of this library.
+library_names='libibverbs.so.1.0.0 libibverbs.so.1 libibverbs.so'
+
+# The name of the static archive.
+old_library='libibverbs.a'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags=''
+
+# Libraries that this one depends upon.
+dependency_libs=' -lnl-route-3 -lnl-3 -lpthread -ldl'
+
+# Names of additional weak libraries provided by this library
+weak_library_names=''
+
+# Version information for libibverbs.
+current=1
+age=0
+revision=0
+
+# Is this an already installed library?
+installed=no
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=no
+
+# Files to dlopen/dlpreopen
+dlopen=''
+dlpreopen=''
+
+# Directory that this library needs to be installed in:
+libdir='/usr/local/lib'
diff --git a/external_libs/ibverbs/libibverbs.lai b/external_libs/ibverbs/libibverbs.lai
new file mode 100644
index 00000000..3580def8
--- /dev/null
+++ b/external_libs/ibverbs/libibverbs.lai
@@ -0,0 +1,41 @@
+# libibverbs.la - a libtool library file
+# Generated by libtool (GNU libtool) 2.4.2
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='libibverbs.so.1'
+
+# Names of this library.
+library_names='libibverbs.so.1.0.0 libibverbs.so.1 libibverbs.so'
+
+# The name of the static archive.
+old_library='libibverbs.a'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags=''
+
+# Libraries that this one depends upon.
+dependency_libs=' -lnl-route-3 -lnl-3 -lpthread -ldl'
+
+# Names of additional weak libraries provided by this library
+weak_library_names=''
+
+# Version information for libibverbs.
+current=1
+age=0
+revision=0
+
+# Is this an already installed library?
+installed=yes
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=no
+
+# Files to dlopen/dlpreopen
+dlopen=''
+dlpreopen=''
+
+# Directory that this library needs to be installed in:
+libdir='/usr/local/lib'
diff --git a/external_libs/ibverbs/libibverbs.so b/external_libs/ibverbs/libibverbs.so
new file mode 100644
index 00000000..bd93569d
--- /dev/null
+++ b/external_libs/ibverbs/libibverbs.so
Binary files differ
diff --git a/external_libs/ibverbs/libibverbs.so.1 b/external_libs/ibverbs/libibverbs.so.1
new file mode 100644
index 00000000..bd93569d
--- /dev/null
+++ b/external_libs/ibverbs/libibverbs.so.1
Binary files differ
diff --git a/external_libs/ibverbs/libibverbs.so.1.0.0 b/external_libs/ibverbs/libibverbs.so.1.0.0
new file mode 100644
index 00000000..bd93569d
--- /dev/null
+++ b/external_libs/ibverbs/libibverbs.so.1.0.0
Binary files differ
diff --git a/external_libs/ibverbs/readme.txt b/external_libs/ibverbs/readme.txt
new file mode 100644
index 00000000..c47af5e9
--- /dev/null
+++ b/external_libs/ibverbs/readme.txt
@@ -0,0 +1,30 @@
+sudo yum install libnl3-devel
+
+download MLNX_OFED_SRC-3.4-1.0.0.0.tgz
+
+
+open [libibverbs-1.2.1mlnx1-OFED.3.4.0.1.4.34100.src.rpm,
+ libmlx5-1.2.1mlnx1,]
+
+
+how to open
+-----------------
+ rpm2cpio myrpmfile.rpm | cpio -idmv
+ tar -xvzf myrpmfile.rpm
+ ./autogen.sh
+ ./configure
+ make
+
+copy headers
+-----------------
+ copy lib src/.libs to .
+ copy header to
+
+for mlx5 driver
+-----------------
+ for mlx5
+ sudo make install
+ then copy the /usr/local/include/infiniband/mlx5_hw.h to header folder
+
+
+
diff --git a/linux/b b/linux/b
index e5fbbf36..5fadc440 100755
--- a/linux/b
+++ b/linux/b
@@ -1,6 +1,6 @@
#! /bin/bash
-waf=waf-1.9.3
+waf=waf-1.9.5
p2=${PYTHON:-${PYTHON2:-python2.7}}
p3=${PYTHON3:-python3}
diff --git a/linux/waf-1.9.3 b/linux/waf-1.9.3
deleted file mode 100755
index 7ad6ae70..00000000
--- a/linux/waf-1.9.3
+++ /dev/null
@@ -1,170 +0,0 @@
-#!/usr/bin/env python
-# encoding: ISO8859-1
-# Thomas Nagy, 2005-2016
-#
-"""
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-"""
-
-import os, sys, inspect
-
-VERSION="1.9.3"
-REVISION="826456a3e8d1d27787c240c6ba69d280"
-GIT="b022313c197614245fae732c0dd1d8167b9ee5dd"
-INSTALL=''
-C1='#)'
-C2='#%'
-C3='#$'
-cwd = os.getcwd()
-join = os.path.join
-
-
-WAF='waf'
-def b(x):
- return x
-if sys.hexversion>0x300000f:
- WAF='waf3'
- def b(x):
- return x.encode()
-
-def err(m):
- print(('\033[91mError: %s\033[0m' % m))
- sys.exit(1)
-
-def unpack_wafdir(dir, src):
- f = open(src,'rb')
- c = 'corrupt archive (%d)'
- while 1:
- line = f.readline()
- if not line: err('run waf-light from a folder containing waflib')
- if line == b('#==>\n'):
- txt = f.readline()
- if not txt: err(c % 1)
- if f.readline() != b('#<==\n'): err(c % 2)
- break
- if not txt: err(c % 3)
- txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
-
- import shutil, tarfile
- try: shutil.rmtree(dir)
- except OSError: pass
- try:
- for x in ('Tools', 'extras'):
- os.makedirs(join(dir, 'waflib', x))
- except OSError:
- err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
-
- os.chdir(dir)
- tmp = 't.bz2'
- t = open(tmp,'wb')
- try: t.write(txt)
- finally: t.close()
-
- try:
- t = tarfile.open(tmp)
- except:
- try:
- os.system('bunzip2 t.bz2')
- t = tarfile.open('t')
- tmp = 't'
- except:
- os.chdir(cwd)
- try: shutil.rmtree(dir)
- except OSError: pass
- err("Waf cannot be unpacked, check that bzip2 support is present")
-
- try:
- for x in t: t.extract(x)
- finally:
- t.close()
-
- for x in ('Tools', 'extras'):
- os.chmod(join('waflib',x), 493)
-
- if sys.hexversion<0x300000f:
- sys.path = [join(dir, 'waflib')] + sys.path
- import fixpy2
- fixpy2.fixdir(dir)
-
- os.remove(tmp)
- os.chdir(cwd)
-
- try: dir = unicode(dir, 'mbcs')
- except: pass
- try:
- from ctypes import windll
- windll.kernel32.SetFileAttributesW(dir, 2)
- except:
- pass
-
-def test(dir):
- try:
- os.stat(join(dir, 'waflib'))
- return os.path.abspath(dir)
- except OSError:
- pass
-
-def find_lib():
- src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
- base, name = os.path.split(src)
-
- #devs use $WAFDIR
- w=test(os.environ.get('WAFDIR', ''))
- if w: return w
-
- #waf-light
- if name.endswith('waf-light'):
- w = test(base)
- if w: return w
- err('waf-light requires waflib -> export WAFDIR=/folder')
-
- dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
- for i in (INSTALL,'/usr','/usr/local','/opt'):
- w = test(i + '/lib/' + dirname)
- if w: return w
-
- #waf-local
- dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
- w = test(dir)
- if w: return w
-
- #unpack
- unpack_wafdir(dir, src)
- return dir
-
-wafdir = find_lib()
-sys.path.insert(0, wafdir)
-
-if __name__ == '__main__':
-
- from waflib import Scripting
- Scripting.waf_entry_point(cwd, VERSION, wafdir)
-
-#==>
-#BZh91AY&SY^:Z})2ÿÿ±#$Âÿÿÿÿÿÿÿÿÿÿÿ]á õ#$8E„xaöÜ÷½¾Ï8€#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$¯}W#%÷;©™‘U¶Ò*[·]ö}÷i«n»¶ú8¥9ŠÓ}öwË:,×¾ä懻vÕÜûÞìÊåï«ï½žžöíží‡må… h­»»Í‘&°t ö^î=;>í{í¢_CO}<¸ÛÝÙ›ï>n8>¯[Ÿ]=ãT$ìß{ß}óï†ÎÞ7¾Ñ×ß{íyéÀ÷`ïw7w>»ì#$#$@#$Ÿf€ô£§#$m÷²€uàæÝ·p#$õÞÒͧCÐöòíkf©­Ðjû½×#C{y zØBöîÛ`7³p …D¥Dª=ì{e’B%t(H•Q@÷p:Rß{¥ç¶6±Æœû«=i{…e»QÛÛxÔvÊ¢•¶îu×וèûç½^#$Ùçœë«Íõ»ìhÝâz={0ªM½k˜õÕîî{ëdäåžÝÓ}:vßq÷²ÝÝ^×ßo·×ÞîÏ.‹m}ìa°îz:U­Ølì蹺I»»Ü÷·yêÆ:ëBîÑÝ•†ÚÙ’DE»½ÞÓƒÖ j¨-;`)èy¡{“¥éæ·¶ô[ÝÝtjT}¸;ëÓ统më >€ÑC­m÷Þv+뺷 lÀ »ëm€ß]xö‚•íÊà-÷Ýï{¼¹ÐPûÞö¼çÆîÎY]4Tû½ÞMw7Kì¹Ùèy3mºãÝß]õçVÕˆ©»·vÖ2ÙÖïmímÍíÝwtÜç·{ÑÞóW'‘ÎíéuÜ¡k®w ‹í©>ŸlwgÄ÷m³Õæ#%÷¶¹§kŸyº{Г*ðªÇϱ÷cÞͶï½ÓÝ}îíµçg>òöì”ûÜ÷¶û²œ^öTæÛ–v­Ýi¼µ€Òw¼ïAööÁ÷yä9Àz‚Š®±Ðj©ØSCs;´1‘îw¬î¶÷*Ü4è=Ærw{3Ó]W©º®Qpj{qïe#ºòØ4 ÝîÛt·\ï#$on: #$×½×¼îõµÞ=×î¤ãÓíïfÉ뻎÷Ÿ{Ýh¹Ñ·#%Ö ›Ó]±GÍ„’×ÜRz­Y–ûÝ:}q]ô;Û/-÷Ýsî÷{›O€’ :=æ·weí«“1^Ï/Ro`´s#AŸmÈû›îÝ“f¯;«­¸ÙÔÇÝöï:½–ìÝÛ·É®ÚÉî%ìÇjë&d F¡ZzÞÇ©ëß{ß[Íè5í‡1<|°åïmÞ̓½Ãn×Rؼ#$À»c]8ª£žöô·{CÖ½nèàîÙÓ/'¼â¯}7ÛÝõ½½Î¯¸í×¼ÉuéÖmòì=ïmŸg ®Ÿ@%Û(PíÌÝ«ÜðPã®ðÞ#%6Áçvï\¼÷ƒ¯@]Ÿ@„‹¶Ì†»€#$äqäÕµõ5âÝÀ=±JT(-»¦Âö.@C»=ãw“³ZÇ#)Ï0é7wnvs»9\Ú’H¹vî›d›­ÍïïLÄgS=«).Ó+“Û;Æ=Vû¸|C<ÛíÈk¯£àclÝ::>Œvfê°È–uÒûk;_v÷£4ë˾û©_mƒ»»q¯WƒÞ;ÀѾ5ãœ4Ñh#$€#$&1‚i€™¦™£TýS5Ê#$A¦mCÓj‚S@„ @M ‰&™&4“Ó)©æ¡š“Òz™<¡è&‡¤#$#$#$#$#$#$H‰¢h ¦M?LT÷‘SõO)¼©êyOSGê†hh#$ h#$#$#$$õJ(MM5jmOSÒz!ê@M#$#%#$Ä#$#$#$#$#$#$$ˆÐh#$ õ<4ÄÅ<˜ÐIê=M4Ó@¨Ñ #$#$ 5#$04ÈM©O5)ùLjbŸªfžMQê~¤=‰COP#$#$h#$#$õßæUZüe ý+Q«cN¿…U§e6C>ª­w³Œ†Ø‘X‹Š*$Š#)Ÿ#$ ‚–Š€¾¡0@Š~ƒóÏƵün‹†°Íhüûz³s_ŠO8§§"âf¥T¼'…/1WXÆ#‡ÉüøÌ“´ ZD@ÐPÙJ É70¦E„P†¬92/>Mšcg£#)"îêUŠ‰‰¦²’®fñ‡Å¨ˆš»ÄMÚ—£ˆ©À“#$ÆP¿w/úG:&„#$+iš6™Ý]U¥j±­´–Õ±­±j£jÕíV–Ûk–$!Ë¢À6‰›Â(†0Qlˆ@Ä@H@õÅh€€ª$F@dAPDÈWl2DÆfÆ™šY2!ˆÍ&™¨ˆš‘,dE”£J)¶“aŠ™’h%5MFØÐa¤°S"4M˜±Œ–™Ej5,hLYShÒE&Éh­IK-)M¨€`›- YA0e±hÔ–Á-¥4È&¤d !E4©CJlmVš¶šÚ¤)•C6$ј™!4DV–´f–*JML•-°MšÌš–¦ZK&&IQFÀ†‘²¶B"ÉE2(Ñ"ED†¢”Ö) Ñ°Á ŠCLÆiQjRŒ4DŒÊ0MC"2ICd­$„RÄ#i(d HH†’šVhɉ£aˆ¦RTX ¨aR*d‚,jR˜I‘É M dÄÅIF‘-šRM&$‹DÊjcf@ج–± ,1RHdÄ)±¬šiX`X$˜¨Ø *$ °š ‘(Ñ“JX ‚a(c$¤ YŒ™„Y52¢#P›%D“!5&ÀHD’RV @A‹,Ì6Œi¢`‚e‰")Fl% 3l³YKeŒd,i¦hĦÅ%’6(•!3 Œe, ©f@±%ˆÒ¤–™AF“4ŠŠ¦ˆš¨F ”%‘ˆÙ)2&‚i™Q£0£YH ÆÁ°D2dÂfPR1 š’„Å#$l“*fkLcl¦Ó$!™©ˆR ÖRÓ#)É„’$¤’Å“fÈÚfb&dŒ”²&ͦ#%±")™(lÉ¥2BÆb̦ˆ©D”Y©d¤Ê) #&$È¥dÑdŒSe  E+)$Ä’e5&Æ‘#%#%#@„cBIY¤Ãh4lf2I„²e"2ÄK6JdRˆ„´„Õ6lZ+Bd’’$-E°Z ›FE# la Â2Š’*,¢Q‚´¤ÌQ¬f†„™#)J)”±FDJaŠYcL°"Q”e!¢jÔÛV‰$É¢Œ)‘¢ŠI™­ÉlJY£LRde3K*Y£¦hØ[$¢š"±”Œ#%!M-~Ûk£!²´„„©De±¶6ŠŒTl™2ËÒM,´FÂ6I¨ÚÈŒ-2b6S*HÖDS Í3Y5b$”ÖJBÑ‚”IbTi…ƒIŠZS&LÙ6 U¦6PŠÙR*•JÌ)³c1”µ±cdF¶M2²%Ifmd«-”¥4Ãk"…2#b´fÒÄ–’™M±mƒk cZ‰*#)6Ú%Tm6M´M!T`¨,U‹bÃ6l&Ù0¢*¤)A(¤ECR‰¦ÔÚ#lT‰XŤ,›i$ÛVËm¦CbÂBe2¦¬šfÈÄ4´Š)"ªšÒ¨Å2ÓK3i¬Ò›TÓ‹R¡”I6ÒÍRÈ¢ˆ[BKfL’˦Y5¥©d-f˜ˆ ˆeF’2Å01 H† Ð˜ÀËQjIQ†R¨¬“M&M%€,R)fˆ¢¤–FD1”I#)”l¦hÐH’š#%‹2B"–š’ȶ4Í4"i!€ŠŒ˜ÃL$d´X! !&ÁBlQ¢M“4³µ2³¢“a2¤¢£bÅ•A)‹3I%”(¤1fmÍ£c‹hF‚ɳB’ÑCHTd2#F2š$¢–RЙ6Œ ²‹$–!ѵFšÑš#)†Š–Y6ci3idÚ6Æ#!’@˜È,’XJ’£4Š”Ó £-’ScPm˜lÒm¤¢(ÈiFM¦V³”ÓS4‘ P"DA*RŒ hÛ41F€k4›%1 LD¤¤„¢ÑªQ¨Ôh£lFÆe°Òa¤’… $‹E(‚6(°&ÐVYdÙ) Ø´d,”³If´l$k¨“E –Z‚¢Á’ØØ(ðÆ6ÊPÖ‹A¥)–B£D‘TÈÔDe6ÖELmŒ˜ &ˆ¦e S)ID•,J"Kjƒj#),”Y*5S3E²RV6µ)ŠJ6Sl†²X´4K6d• 4˜¤£$F#)É€Ôd™Ù“)²R›3bb)TU•“Y"3I5£)ªKbÙI4Y QI&V¥”Õ)ª$Ôj¢¬e1i5² Q¬j(±H¥3#)dfDÆ•"(6fÌÖˆ±¶MÑ©*i­¡•b¨Ô…#%h¦k4ÛE£QcV2kRde(²³h•£‹2ÆÔZT°¦ÙFXÔĤƒeFÅ•KclYš“¤ÚÅ&ÙLÍQ£hÃH¨¤¢­¶[m4²&XY„‰41ÒhÑeI6¤ %X­Ø´Í[F5¬ÚMše­ˆ²[SSl…µm¢Q”P26’cÃYS5l²*(ÔI‰$ÊI‘$5“Z,ÍTÇù?Îü—ùÍüç5€ñ÷ëíÿ1+J´4}Ç ·—ýL”“ üpÂ(Ѥ¿–¹¤²ú¯|×óž«×7ó›×«zÄT´ÇþreŠÀ¢hó%qÿ†µÄ#ƒ#) GÈ•, ’H‘0(£#w»KD¸äß‹1ìÒÿÏí­¿ðH’…+r¡+JK ÕÏV^=EŒM‡¹vt3´Õt­˜Hc¶&Û¬-q(ÅË®Ï#)1údop Œj2q!aSM•Ê×DûêäG—dï; _-ñâï‡>µzܳ®¬…ߪêní®MëqóÚæ*ùWËÑ\ÛÑssM¡´$ÐÆÄj×gx(Æ0.¢%ˆ½Û ¾W6Fo[šJ c*è-5­X\r¿õì³üèJH¥¡½ÓÕ[0YB u@©çRÛ*€±}ônð‡T¦\mâب¦¦Ð¤R›ä¬k™"˜Ò»Õ$¶#%Ñ1ˆaöi¦`´5æÎGÝí…h Â#% 2Ãvü ðà¶~†#i†Ë:óí‰/üîÖ0…•¨v.â©ŒøõåáÎt]ÆÍÌ’QlX±Q~9}³E  ʨŠO]Ql÷3ŘVnÒ@)¥Á¡FG8ê‚>ª\‡3k†÷Ö\#)MS.‰mAF#i)&Km±ª4Š4dƒ{œlË ÔÁŸb¬9bÜZ¨¥a7"rpaÆŒ“®éG9¾[Ëοտ¹¼¯*1ií×l}%s¥ßC¯ªo0W좑:5ýN4Ç?ØóÀLð D¯ï¢™³ZbÖ#)€Ž*C#$È JÕ £h§ô`׶~|AÖ 1ýz‡sÒ4¶ÅµCòÉçÚ¯S¿»?Ã˽[iq`m•ù,,¸žWTEÊ5R›n%:¼S“#%†ïãfã¥m¥CXÌu¡ZR#)¯§?ø¼«°³ä›VÜ(äß¾¡[PP±Â0BÂ2'DÓ3n ±j?ºÎ‡§hf$ØM”}h¶åXÿª‹dê˜JJe¡Ýe,#%éÒñôvCÕ¾"[~¼å0’Gá–¡ÆFÐÙŽKñKÑÃË{5Ŷ…ÕEkFÿ­ë£,R,_Ô2Ld—ãë¬Y>/ÓÕäF=Òy¿ c¦Dw¤ßýg÷‘O';Ex”Ö#ub±–õb$ÅqO<Jìiv¦ŠE4Qxæ’Æñ\¹¤YýŸJÀŃàÔF(W±±ŒØ÷~ú™¨(-2„AXW··€ ¤CDI±IóÝs_umtéËrü~u^Kí_sáz±êáƒEËtÛbóÎýôe(ØÖMŸ7rÄO´aFEöT¨²EE|jªJ™­nÅy‹‹&¾Ção¨,–üÎÅÕAhî½F[îÍ ŸÞÔº…$¦˜ú¨[fÇóÔ9ä Â#]+k6CÉ­ê™Uôï×y¹[³ dÚû» åó`ÜiNa+€ÄÐÜdÜEhú±²(–” ´Â˜('m@8ëJn†^®ß%ÊT—Um*íÝV)økÏ!—~ú·w’wvÝÅI<®ÔÙ†¨Ze¤‚0§Xôq«¸y`[&Œ™i.šÂYy¹J©ÛE½”)Ã…Ëb¨‚§>Ú“Í1(Ü ©äû[ò ¡êãˆøFðxŇ¹Ž«)¶åßÌÔ;a¨þóŸFx±¾©áŠ®¤8¿Vq'^ب½­_o—•b©))…#J_šF ýøíLCPqŽœÿªÎ×ßtÑõûhˆhß#)èÃþw²¸3:”… ¤GÅ S“Rߥ}}{­Ýj5(®ˆmìã繊ELbfNw¦Þ|%$¹ÆäFî¨G_½–ÃE‘1½ê RÖ¦·˜ÒÝúndM4ˆópÓò> 0U2#)B­t«$Î$‚TL¢‚E„yêj·¯¹Y€Ù4Ã>ˬ×akÃâ]^üÄÊX綱Œ¿ ˤúò¢VŒóÅÈZ¡=#%˜É0Í^ãÖÈ_•)0ÄCz“ ,>§u@¯×ñ$nÅèÊ£ªL†sœzgnú£êÓ½45ê>—f|=ý6Ó•ºë¢´f©2—$À‡éUܨ¶åTüšL/ÉÉPî¯i&AÕ-)ž5XJN{նМR»• JXŠæîú_üþÜš@C…wÝ3û8Â3ª^ç9õçÉ—"œpã>Ñ[øÉ„=ìë"ÓädMǶf(öȃ^|oH0ïø´Wd1Š’#%¹õöå1¦Å4è=Fj/n{ˆŸ’GÝ—ëµÍ¶%ïÉb½îχ$™Yð«ãÎýé8§~tñÁH"F†'ù«j„ZÖe¼ƒVnœÌBBéùH5$Ï… 4ÑÏrðÏ#)«te¾m ¦ÎUƒ)çu<äíü\Sía®ÓÙïÎ^?«ÒùävBI$Šƒ‰wqÇ] ,P‚S$ÈO~´]¤ä½ê)@Àí1´‚Qƒ}ÚÐQ±uËApX)â.<Ü ^£ƒîñ‰!1ÛàìwÓ±H ¬&€ö7²ÌÔ™ºt­Á"Õš9fŸ'‹nC‰±õ‘8ÐÇŽ-“èü!ø?¯^çxnY̨ö‚Ó»qô“ç.XìãÉ8ÿc¿Õ¿nÿñ¼êuß{ÖŒYTNŒ X§ -L´(¤Rûî!sw½Ôü=u5A'“BÇÛ—æеTµEzUAL¡ ©~|¿Ãyõ{Öö>?·ºó’WÚÔÞ©õ0¤X£eIÝEZ(øPRUê…÷³ÐOÍùók>j×*kV¤F~¶S—ï}ùãôb÷n–Iù"ÛWÙ:n¹ºŽgT¬õ{mr00#%}ß–†{âÝüµWW~¯f#%­fƒY1„ûrÖïPÓ3óÒSþ3°÷iFK*‘A‘èÐŽµ'ÚC#% áÒÑŸ„ÅSSŸ3ƒi4qüÕp3?Œ‘ñleWÞAV;”¿Œ‰æµîÐi ð҇碑B`vë›DQc` Å"ÒhŒê_v¯Ÿ)ù74c皧ٿvp> > *ƒ/u† 1ÆdƒmwŠ1üFvNç®#)oÑvÊcËíšzxõÜtsã±Ä6”$aÁÁif©>‡óD˲Xü]¡2”ô»üž»ïg!ÆJƒu|/¦a”Ë%{q÷™É‘òd¾ú¤x3[…=Û\ÄJÖì‹X>i5;+HÌÄES#)'_ÂP[oZªCÁ6ÈJGïo,r‡ôqÓn!­és9õ»À‘]ˆUTÏË‹ñlÁÍÕ:ãlà ‚W¥ž.!û¯+¶]Ânwhˆóˆ£›#ŸF¨"(˜w³àÂìvôÇNu¦œ¬þíhòì¢#)h]ÚD´ñaK–RŠÄ:3„aaÏ]{|w–Û!\kbb·$„‡ýŠüø¡Ó@]h”!7¤ø'ö9eu1ÉÓ"?žÿéggœá_&Í#)ª(£”´û¤5D<ö,ØI P0Ää‚“¥š:îtTŒ’B-)pàÖ"Ñ­‡cÐOÿQÚôŸÛwsÇ1„bäÎyÖ>5°º?Îáf1ÞY°„ßI[Q[9ˆâ©ˆF òüq÷èrÁBj¯ªîÕõ64âïáÍoUäi b§uú¾}Þnšj¾e­Ï…“?uOÀt ­«ÏÌÀ\Š#=ïzß#)²Áxá‘Té ’BØA‹Çá/.&«s¤fÏÃéÙ1^˜ iwdršòä¬+—onàZ•¼Ê¤㑘)#fõcêÖ¦ àD 2ƒZªHrd™üžrä¥ç]*ªÜuè³´ÃiLŽ7–¼žûdzÂbÑZâÝŽü4úSo"ø!þžÐÆ|¢+‡C÷òâZ« ³?”æ¦;ÌÈ…—{%ø‡'õqWÝÁÃn4A•@¢õÞk4 ¹›9ñ¡E†tͨ·8½ž^®V`{u­ŒU4UnvÔËoÍAŠ"<±ìÁ–~ÿݦØþüQ>Ïë¸,í~Ç Ãù±e‰Êb­â"(3Oéׇ‡N{vçҋó¦»÷dð(¼šÔ¥•TqïÅ“à:€®•\æuð¶ø=|.XC(¸{<m¸!•PÈýT—•óŒàuÆœˆ•XÔ±DÆÔ3Êi±Žëª·Y‘“Ž’‘= §g}Œ|ªª˜ú¡ŸÏTËÑlÕû÷âII•æcðG,Ž‚Û9"}3ð`qcÜQñ“| «LOÒ|Ö=ñ_#†È+2F¹–—ßâ«€Ù7úÊ\]s—Ãì¿-}wå|[ŽÕÕ¸îáÊ?NË®UVzX8Úñtøð³5µœBÙ–z3õ³ÑÎg|O++àî”Ö ¸Úä¶A(ÖI7š5éQ…Íûáµ6ÎÎ5àä‘{zÕ›¼n‰Zïs#)¦µþ:û°yQÚÌ‘Q‹Ÿàé›{ª¢Rêè¬)\]œ¨ød9¹?7MKÁ“M@È•àØœ™û»kfjxëE¿T.õ¿Û{øê[i¦K­`QèAt‹‘ÅîVÚ­B—Ïç‘êXû^~ï~Ï)Û×ÈF\†¼¦JýÑžñÛGYeA,·èšߎÁ-5V%ý5}#C@¿Ru‰p݈—ý) ¸J¿*–ìê#,žPi®ÔŒgë‘þ®#9˜1ï—3)˜õéf#%6•T-_wK EQEž¤ú8 娾ì…yú"½²ßÖÀ¬[ìi7FJ(ÙSôû*LxÑÜìþ|ñKXueØRwg¨vñQfâ.¾j ‚ ãM±sX²O|#)x#$E³[ˆdÇÃySÊïPŸwg^Û²5ÎÉ|Ó¡ÄÚÃœ¿íñÆlm £H˜rBiªêIŒ3Gž^|sÏQï¾G˜m,oD>Z‡“#Y-O˜Ñ?S‹ízï…÷Œè…ŒmUQ9$¿^,d/W¥Ó ÇF¢e¡g½„ nϾNÕÚÜAnèýFÅTý±-ë \íg·.ž!±2Îaú {µ¬ÂØÅ»ƒ—󲼋H÷à£7ê½;ï¨Õ# º»¯…”Ã@Öš»¤YÑ©uÝBQù¬}˜Û׆fTH4˜[òo N?Lµ&° TTM*`½)àÔ‰ÍHŽ#%*uf3‹D—uK­P2‘š’…;­Ñ‰<÷ö¼áWÅñ‚ꔬ©«ž<ÝÛ›Äír)qÕw']a}¤"ªÅ–´Än–ÿGÃÂÏPäËÙ»w2 t•P¥‡gCOaRI;âë"òœöþË•ÑCÒfpþö2l¾§ð?ph#$šsñÚhÓÜ6mË¡r…Ê´uÿ·Ý/ã]îQÁNÅÌ”ú²•ôðáiŸéÖNoxÇë3¯n•x†QSÆPªo½–ë ¢ÞÜÅIV÷TGo(Ê”Æɳ{òk»[aÞwuBC5˜<®±k³}£Nؔƫ7sæ¬ÞêfšJ\ÙBOÔìýz5ÇW#%É#%‘ÝõqK­f:;˜_ =ìû+­ ÊºzÇÛ&ÛºýðÏng³øè䢓J)›Žßù#cý¬¦à}`Ö1|qª›¼2Ý}ŠTÈÝØ#B5ÀP^TÿMNÆõï¡·PÐÆ1ÙT  ZÞ>2ÈÐ þÇÈý›ýùæÿ„0,å ‚Ñ¯OÓ-ùgŒ¨v.>]Ô»Ë+¸Ù©QtB¼Ý¦;+‹žÅyig•;žó~‡ÿ¬Y)¸”gÆwª¸Îÿ\ ÜÎ¥u|ž¾Ì„šv³´‡É÷½¡ªÈ÷}Ó;XìÖîJj9wmìpû~.ڻ㠙¤N“¾wÔ÷x<§;'Î&wN—| Izç#%ùv[ê…E‘[Ç(–JÞÑ#•97µP8dæ•Èj"ºÖÿâ¾™–q¥Ã›~Üb§·$‘ô[@ÀoÏ0Û[hI†Úªø¦å¥ÔŸvãâcE`|ð?¢„œŒž!½¾~&:4†ŠqÚ¾.4L§b½Œ3·º÷ÆlJÁI­ÈN¢ÿãTgßåÍ9i–Ÿ>nI7Šèò›»¼»¢.8“mSZ–ï´Cˆ|ü|~°Ñ‰±È°Õeê¨Ãèh¹Ð4•Ó“ÙØK7(]…nêF!§ßżèÚC!¬¸'|%Ž3Ú6[+ŒaF¦©CÞø ¥Æ\¦ÛO‹\m‰”D¬Ýdg¦š,@™Å^@ÁØ × XkZqÂØÝ79v”¤^õ#$#È@ Qä^ jž½êŠ%©²¥ãm.ºÕž³e¸¾Ýu´Wƒ r¨@Gr´—ŠDŒØin¸_¨f!b®Ü[fÜÙ²RqÊå°\#•‚åa¶¨Ž'ªôßwWÈÏ;ôð®|øt³7åKýÐ'€´^ßÃ×°Þ¶“³¦qÖ–ðJío,×±[”Êí. =µógpaýH­}>!×ó±1ÅG“é-ÿ.üñÌÛU5žM˜Ûïî¾#)|ð$RFÂ/þš:¼²°LsH!üCo:“C™‡D†îOõù7jŸ%Ĺ²L`ÔÄkm(0@§DÑ@c0éo±â‚wÇ¿2òFnž°=\j×ÎØÎÒûqø±œ/]_O¦Û+çì™ÒS˜ÿ€XZíb•åGÞäpA=ºŽ¯¤5¸J8ù•gˆ…áø—o>öë‰W›Ç~{vÆ|Îؘ|}XÞé›9N¨C?W=yjÈËEwˆ-ƒ€±$6¿Ik¯jôn) ?bÇ*dªp;ÇX^îòÉ[ǤÒ'jˆD·x=Ñ­ª*,|iž?L$HyļQXþ/·)t;s¹¨ùc7ãÇ”¬Á¢Jiä8C@ÛNlŠ~G‘\ZnJmG5Vê]ά‹©“#$¥¾fƒN†²½yÔbç 9'Á¯"Q¹Êýa`o¹Rª†â!-LÁÄ|‘T¶ñ´D;ŸDYßëZË;™Á¥¥î·»#)#$–‘·z&iÃíÓë»—f]¤f4 R”þÎwzzÿÆnQ¡³íÿ6íßÆòçB&ôÌ/;•ì?PÈcÌ–¨ÍR‡z¢òÉ Lªö‚7Í»ówü¸íñ#$Mwþ@ÙìK‡ÍHï÷ D @HƒÐKüáOG 6)Æ0]¾Iç¢iJ‹$ˆŠB´ŠÇ]¬0óp4GÀϚšÉ;n-Ê|ù»üÿ-#“š±¡#$?‰Q#%[Õz<Ü𢟳6vã3`u`ƒR¨%â0§`gkžÓöät#$&Uš†È„‰D( #$'Ëû«ÝöT+£RÚéÒçG¨êÓ(HzƧ`>(`óÓÜÚe#$7ú|güRpFäŽcÄy èRuCöÍ—0ÿ¯» †w*µÕåD̽žuD2#)ŠAù*†ÀÉ&~íêé1[™p’»I´ˆ€òsF¤Ž§ßƲj³Ñ¯¢w|‘jG®m!)±„gοw×Íi(Œ2óøhêN>ñÎvq­ Á8XAEçÙÉèbG—Ç_‡ k’0K}´ªñúÃPˆ­`‘Š#)ž—1!ç@ŸÅ9À"&~X<@Sàä…“Ïé·Õ*c*&ûþ0û`€v€Á¤&Bmv>íÂíôwçõrÃJÃÛ^Õ2Åaðžhk_Ãú§íùÓ Òµþ’àØ6ì×?¦X>á24",Œ‘Ï}¤r$nZ9hNz?Lî›+e÷ég¯ÁâÝ”×xþ«øwØjS@¹™aª”ªç^EÈC6QŸåk]߀Ŭælôhå}¹g§MSøÇëáf²2|ü5?…‰Óùy\á(=cŠo.z˜:ë©1g›ëûõ¯Íïyiœ=›ð`>#%9å•Èe5­~¨úº¶¯"ˆ@ñŠ'àaŠBÉ’Ðì€ãêÑK¦$´! @`£‹”±ˆ|C‡ûWeȽED=nª©ýKnŠ{Ù(Á´É%š¸yŠ²7|IàÇ÷qþC9UWÌ<µ N×VM…ï6ÅÉQ$QU@›yhŒéʺ¡¿º‡fžÊ½ä‡å½’&ÊëϵŠ6ØŒž<˜à¿lûœá™¼“J°ŒéôZþ#ÏÞþìiq¦´-8ûǬrÝ“[c|JÉ*ÚILw]ê¡#ô}#?ÑÇMu†¤‘µwžç´Q {d]=K±þ¯ë¹hQl""^ûìŒa'ëïãîW3 Ÿ/w«3"ðªAGHQ:Üþ=˘Iéïy'É0&½i³¸påm­`zl6}b©Œý³Æ,-˜}t÷y]¤_õaôÕðèÐÕ†#)g…â~dÿ•¦Ã¨úBbb/˜ÚØ ¤­Ï›µé·ó¼žäi°Œ4öçëÁ½˜GyÈ@o³ÑÇ«„0­¡‡û²AMObô(ñm9N.Ö5Niݬçsc‡ïÌ“—úo[´ôúœÂ½ÌÏ¿—@~÷LJ&hŽ”Ñ3òÓ;³Ì#)JK6åô]ô°£7›²¨y_nP[L§ Ã0ÏXI#%æ°(“ÊTŽ‚ò#$KšwV~byþýú__>ò·”ÿ„<(Ù ¥¢y¢™ [ˈïÅÝ;´Ã`D!õŸ§ßuy&?;¼y›sŠoz`×ñ)Ô°;²ïrÃI}x—²ð¯uñ;ÿ^ù#%#%®š¥U4Ñ­#u-QF±•OX}ãñÞ–xí)ä0ëÊqZŠ÷û–˜A;â#PßC"S …o|d‹c±¼´ÞY$j±©4mÓPä¡Eù¾¯sp¾ÅqMYaÄìÙÌ;ÞÇŽÑüšÚ‡ºD’n;{ûnSÀ‘Ê;Õv’…¸ï l­ÇM#°ëù—Æ<}€zh5…ðþ¨q›Jõ;[I¬Cc§|}x‡UFütãé¦íé~Ÿ9#)9]Á´™„ Ï·_T¼‹…c§ñ¾Ü+kèmi¥¾n@±ÙÉÒ‚LU°íî¸õ¬CÔgüqÎ&öw¨vçz5ˆêd!¤MÉœp°•¾~#9ç#)"hÈö )¨Íãhqd.å1!˜·RÆÙÃ|ZÈ*ÄCW—¹ñ¼½#Þ¤Åo}žžg#%(c“þÑÆ®gœÏµÔhwØ¿â– L¸ÇeÎlEw‰*1"("*ÊM™ô7ƸޱØb²#)lÛãüá#$cTÃÙГ^ÝÔ vx§RÇýÄs‘V*-æp#%*Àºìa7£Ê!¥^Æl¯*Q0 "Ô4Ø}Ÿ¿Sê=ú×ÃàR³Ü÷ê¼Mõ×ß} éEO £µ¨!Äsü+ªyÐAû–µhˆéY¨Ò’yv|ŽÚÝÂþŸ–ĘÙϳ‚:ø ‡c¥›u]ˆe7g}&Õ¸…î^¬vÑœ_®"#%œýÖºs#%Êι}Ùi‘©¯ã½ƒh^ßÂìjmu¤PP;ÅGY¬‰—Ш ¡ö²Œ™cXV¾J’(‹0V“‹-ÎRprÁY²²&j³F²*)™5$.ÜÌëwéd2óÈX㊅Bå|ŸÂ8=Ö ³N®Î‘÷^æ-tu ŒâÛÊ5Š#%¸ég³Ã¾p˜|#ƒNF"ç/P”ÃòÖ±(z¬¸Õ>:¾—#)Ÿ•ð‰Ëøë±ìùÎÇZõ¾ŒnÏ7!#ˆÇ.ŽŒ;•±·£G;ksc³änžž~çöÁÝ-ºâm2Mä½Ý\*OLU—6ú²‹qÅa¦¨ˆòê †v²#$Ê#%š$À uxê"’ÕužòŸàCè–}ó9©Ï¹àÏCû¯ãKM½„.ÿ*ÒÕ}¿cÓ{9ö(÷åù’îAþh5³Ý}IŒœqalá|QduD+661Q\WWwž.מ9ˆ±ÍÑi’’ "‚Ú;_—¦uÓQ½Ÿ†õž¼ŸvpøéÌãðy!d ¤¦EU#%…"²ÌUa˜ cQ:Ɔ-zá‹oØTÈÆ cŒŒ0Hl±NÄ2p°jîŒØ§§>YÛm*vmše©U7`³JoÍJ}*28ô”RrIû¦tXìv/j¨£î—`ܪ#e§f†hMXþRIB⃱‡0,®Pã{W”t#%Ö77m—”Ýß©²îÇ(uôåêXubM„`NÍkû¥ –@¦È‹“’€`V™)¼ôå¤ÑmÇ1 ðaÝ!aHŸ#ÊãϵÆLëm_ ²>aŒšEoõI½ªquÛöˆý}',»3†y_ÑdîÞyÈÓµs½ôÖÌ™¯>ße«8~±…ZFçD·`¡<}4DqKXÞ§÷jbhÐãA¬ÃRbeç}"hC’æë6Ç[1KC”›Šb¦¡H ¥äe8ÆìU@SFäÃÃ3G|ì×C´.Ó7_gžµê¼[•+žÑ þ xÌ#$CU¡T«R4X^ØlT£ x”`aBÇ—£œw÷ýo¢?M¯]ùó Hl’R±H¢6… /QjûÊóç­¹¬S"ŒT©¨«à›nhÛzÔ[è©çzÑ“%,HŒFØ£U†6Ä@‚°ˆ²+–ÊÎû«.·›#nwØ-³ÛÑÙ´ÇEÆ>³{§aûŸfÏŒsF·^ËìÎ}71ÄŠŠûVon‰—á×î¯gœõÙ½KõzfmÁÜè}c¶iPÁ;EÜùû“ó£UÚ“^º@ÒáúEŽxÐ=Ú-„#%+Û· ‡ë·©¢ü”‰:»—%˜Èfùf<]åæ¶ãVC»I:~ü,Èû°+>ò¦Õ芘s=¶¶ƒ Ë–f62±WS+DVFÆ”–ƒrA»k&#%}+ž_¦%Ãk&܉‘¾Ì‘®ÕF4TàÚÆûr-nøßžÞÉÙ@>­SôV¸ÆÅéA³ê8z-Åj l!³ù`3XøíãË”±õ¾«ŒçàûáÎÄJ¦_sÞª˜ÂtÔËÉ8ÙïµÔŒajO#%@Ô™¶¥„ÎÏIü,ç#—úú¼tÛ#%ý²ïç£÷_À_¯CYžó) AÄ×GKh¸Ù¦’•ÌP"é“ŒgðÓÝ-Ƶ ¶×iDzÕA¦æƒ Ð^'v7‘³æ¹1‡c'¦ þ!Î?¯L<1ºmË1ç³ÏWëW)ñ²²Öˆö6¨Œ#$Å<ÑöšØbR"¢h³º@k,± nu¡\ÁéÅ*¥r%RRkŠMßÙW §Íâ¥Ø7>×Úòø^”*â÷Y°Á(&B"¼ó<9«"+ ç+ÍÌA¢¦”Ò0Y% ‹24¥!¡8°‹|:÷ryvëoŸMQ»ly7°Û#%<âA7ŠÀ±œ2A¢„Ò¨o.¸…Mh©4iX,4·AšM¦hyš £MF¦¬(iã0Ñ ÄØ!ˆbÓK³#%ý{Îú '„DbšCx2¹Ô°Pâ²V9£ñQIŸn5U念ƾòé‰\£Hñ9¢]§—ÉøMƒ¢ÍfîîŒï å#j¹Ë}zÄ®0äN¤JQ0.ˆò¸á§#)f§ð#Æ©”cø}¯T̆ìw‰ÜL†ëd²Ù#%\×+6 6+²êÍflöfþÝü¡ƒf; M;Ã&òcÑÊo9Q¯àn@°mN'<à ÷wçפùâÍ"Ê#%_SF3Z«¤Ž­§ÂÿmËÁÓ %&DüªªSPÉMسšÞ)61‡ÊDÔÛø}æ#)6^ÒéSäÃ,lûEíë«5Çìñ“ñgÊq­ß2¥Aüí+/÷;k¿\~&Ã1¼m5ÉÉâ3‹³eäÿÍŸ«· 2c@˜4ó&ÃAN5“ÞàØÍ› Oð·rg¸?Zùuf/œçw#$ý„kª’m3¨ÊBvRRkµÒêQR®%!©íõ^RôÙöoα¶éÈÄ㓦4±Æ²¥$@-ís‰~#æ»­½Ë€>îÍx3›µ–(Â&†’èÍ·.L«˜Óca5"2i¿OI_\*·÷‹˜q}/l°Ñ#)¨Dë‚sÙˆ7ôk·Ÿ ÏR%O>FÂU=Ø£DüqïÀ*l?óæ#%¢ r/y®vïø¹ß÷r=¶*±‰ýUm´ˆªT2«>ՆبKñÌåœÑK«¡`Iµ®­\Žh!ÕÚ÷-!°çS&Hô ºX€¢%FÜ«\©"×6æ¨Öæ’ Á#% $Cç*LDŒ1ƒ²Ù¼» A w ó¢D˜0‰C%ŸÍš“§ç3kP q 'O¼.R†¯ ^ŸÂÇì;¦ÙˆÔ:=AIë=g¤&^`ûP€žÿ_QðUŒ3–ƒ°ï̼9Wxð-¶ßFà…òTâ¾·®Ÿ[W¶äã@Ì+Ò¥¯¨pÉ9j 1œÌ+èöéýÚGä4õîÿfÞ^„lTbƒç)YO,ÏTǤëÙ7 —|5y?-·ûµŒ¶Æ‡?žçr?C>€fú'°`À«eŒ@k(ã|‰ÁT>÷ŒLþY….?²­æ3ð?Ã^jþ[Æ[]¥)WbR#)²¦VÔ'è¤L¥Â't¶t¾¸wÖ­»·ç#%—JÀ¤rè^’ê ÙôC‡GÃ`È$[…°ö#%; îꦙ% ÛªéåáeŸ};9y_ô뮎nÌ>z#$×ã^hk>50ïø¶êÖŸ_Û«ÑUZî®#%o‡<XDa„F³×¡tÖ1&.HGk$‘å<›jyø.RxšY`ý(²¸ëô|׈#)Rj e™sHsRKŠ0 ³@Æ9‡þh‹ÆAõ{>ünú¿œ×·Ô`ÓöIAÅa\óÏj¯%â–Û³Š¶£a–6Ý°¶#ù?³¯ê#$Ú(æ#$KÚïãjÂ5&ŸWëü½¹ø€&zê$I#_±Ë]ßλÁöi[=‰Õ£_0QÀD`G”¦×r~‰“Ûb,*[Ÿµ ŠfÔ–OÄ®dÄl©bQ~‡‰£MŠ¿¤_mÚŠ0Áª”SDB¢ZHñ÷;b[Zþ€ý&ËwúÍÛ0öÿ1Ô?À"ôÙç#%¦ÚžVÐœ tì•"»#$þÄ,L ³î¾¶hÅlX¢”U@ÐA¨>AjHvÕå¤'g»ã_#%ߎ'ª{e`¢Ê%£AÉãþ*”’‡Ï§ßýKÌí¬#”J8äblÁH)ø¥0?Üa$¦ïp7ùéMh”Í2 »À¦rdZbÔ›^M¸†Ûʶ¾¬ª¼š#$b`ªª¨ˆ’´aùfãür}é‡Òí©WU®“)Œ"#%´¡Ø¤ ñ_à>Èé•þ¥mpQ¨+™/oîrWKCðµ`ùFY”þýX±|°=gö´º˜`ë ‚ f˜fJ®“ïÇúç«ì4…Ú³Q3ò¯Ð¾ÔÊ礞;Îãq´„ñTr…@JE\`¤)³Qå]¡¸€È.²wGUbw: Ý¥‚Ðr!&˜_Q?µÃCAêñӗι<_‚>(œ¨Pˆ# ì¹4Þ°È¢¡Q,;x…#ô—P=|î˜5_¤ÃðhÌöýìYC (ôÚ¡ù˜~-¤7vtªÂB‰ýU&‚ b$þ¦ÐøïÓüû¹h ýVRßì‡ñ#½qÝæý“lýÞߌütþß‹¿¤cçŸX>ï“Þ ÿ|9]äÜŽ…Ü *8yòÞ3F¿Ü>šö²C%^ ôð<Žú—7Ž0 ;ˆùcIý§?¼`‰šSH±ªÉpÞaº—}²W‡ßWG#$.œ[…CÊ.ȃ]‹ƒ­oÍ_²‰Ó8O}Áˆàdó5¯·*׿÷AFøéåãY­+BÇ]Gãö‰å_Ó»~ý‹ë+þ=Ý‘Ù[i’wåéýrPi¯{…9˜ovθ$l§tFq¸›J©Üsªz¾;.H~ª¢ø1¦ wP]·©{Ô1Å”¿³­,>*4ÖßÓ #%‡-à@zô£‚½Ê÷bè6Z‚HRÞ­úNˆ»'9Ürw¯šºÆ–çqðp.Q£t™-¿ê3!Ö…!USܯ Ex4ËŒ]°ˆ•”ùˆýiXœHú@òœ†€÷ë{ÊnG#)ßB0T ïg#$\…ãƒj?Cg¡ã¨P2šD0*¥ëœð˜dÅÊ…àH*å,ê1Ð ‚“«;Ä…¥2˜it.0TV7E3'þÝÈœËU½¬©#$à„¿~ùv¤9±wÓëï®#ì±£ó8ÙöîÓõ@ý–u$}Kiðd ‰8)\GíêQñÓ@s”ÜÃœVA„7-Ä—"ÐB¼¨$ ÒGòÅF5|û*û&伄¿¬„$PÖIq.Î#ÍÙ‹jF• ú]ðgû?ÏüÕlÚ&;Àuùè{±ëLabýkÒëßGuÒ„ItV+?;´išh€Å ÂCc뙧¯Í ôÈu5´XA"µqÖsÊE€¬köÞÀU(&òÀ1#)Jó« pˆt± 4Ö<¯aüî&Ñ›²òIÕ#$VP”#)ü•<Ê.ÍËWTó«•¼i×X’¹pK®r;møÒƒ÷¢!T›ä.#$-?„…Ggˆ(,Ö¼ác#)K@¡’˜©·¢a"ˤ Þ)#$ˆæWÛ•eŽ(ÏCžˆQÎDAB2ÝÌ(‡k3.9ôÛü¹©ÕÖçý]óy>‹<~Ž]xr@ó õOßÇ5?>‘ó~ÊižýõxÛöÕï…”ùòÈ.x²ê¤gð¤h)mU×~3éoWˆsLIÝ‹èéüviÝn›ZÈãn\ÿd:ñìÓîžývMåyaå»nl$¢‹*{~ŸÏ„0ËQo!òêëá/ë×t2ªgSÃ~¼Þ9ÜšÝvÙtx«N—òÈSsŸòݺ_%ß³W†>SH¬}ׇ s/.Û>hÜàÙYp[…@MÝ>zßO-¸ù1²c«Ž^;­ÁãËË™(ýÉéþTzû¤ØB£Žsô|®Â#):³»›–üÙ¥F³U¼GÐôÛÇÒñÁMR¢9©ãÌäï¦.×W"ÝÍGNÙ6~L: ëÿÛ²w¹:gŸÊ鶵Œ¬®ðüto›ƒ¹þ¯§MNßÍzYËü¥QvSÍðpÊ%Ñß›>0¶—Ð2Ž‡þÜ€Ôu#?”Rî¸ÎeÚ,ålc—A”])l·~9- ×_mT袺ú…V¨ºêèaMõKMTÐ#:–#%™QŒf=ÁEbÇ<Q–±¹„\®ÔàÐy‘sÊ®j>¿C“/UXæ¡vøïÛnš{w×ݧW”èÐMr»O¹þQêô룯E·uÎŽMí†ÍYšÊrÓUoæXÈmß:òòÕ.^líI€ãßΧt•ˆ‘™ü7*¿ÜêŒuûó3ÆâF[åÈýQèù:ÅŽº9/¯ïx×ó„Î<¾>~UêûÍßÄ{ƒ ±ßlèˆù!¸êÎ1ˆ»«L*öÂЪù«¿#)oúqòf¶¦ëÈSG‹«Šº¥ðY˜|Ϙ`~½rè½™p¯ýCQ«ì•¢/wöýuë›MöÇž(—õ»Ô9°¤ý´÷ëªêqýTDŠhN6¯»àÙlÓîÿËùfÀz®Ž³w§ù{q‰>Z³h0Êûht ˜WrÍ´×/ÃÔÛòFÏÑñ8P3Ý\3w¬7{8v¯%ëaÅ À©'X!y-îý¾F´Q›­Ëè©ž=»ÚìÍüpiúøÈFš$.WõòúÙßÒk“€+û#)ï÷iÀÿ·ŸoߎIH·‹"¡Gu¢Ø{/¶\šç®uü^Ÿç:~}c“}öþ'ßòü¿S~÷æìËMVòC#$á7aéóÑ»Ç3}ÿ˜!’¡WSx5-ååîaëü?©‘>øÐØ’×¢‰TtvQWOâ>°µÃ{÷H¤»ægr@¨0¾!ú¾/c@È}o(>T‰Q@¢£ BZoe_¿@_Üóæèêç^ÜßG§m›yÏÇæöy;9ñèx,ͧîç6á,ó~s­>®wí`D¨£‘¯Ï›GÃ+?oÒi!sbÿ©zu9yéaúìä^Š9¿,Û…¿?G zGà6ŽaáÃÉÜ?xP·jº>#$;ïÏ¥#$QúŸÊä“ú‡hö~什ùÿoá3Çá+œ1¹56ºý´ …7‘' ÿßk…Nªøò-cÓ¿w°ù?M9'°~9„Ç_êññ fP>üÿ'Ví{@ø‡Ýô©::÷0EÞ¬ð£ÞW¡ì8)ùÈfh$54€ü¨B ¾ˆ#);^º:i#%B$ÂAPoæœu¼^2±ôôÿ¾n±´[«‡ÉúHñÛ/ab¿cpv#$÷×ÕÅsr;÷Qºš ’ûÏeÏÉB(÷‚;¿_Êê¢&H;“¢ø–ü×yhÙòé[º8#ó¿f#Cé[œOÓŒÄÖY#$(ÇaPœd%„Õ#ê`Â5e.CŸr’fH&Ú\Mw y+¸9© °T ñJ$s•#)Õ½uWœò_·<gLkˆoõØ`ìqÊ®[©ÝhÜeÔ¥Hˆç`Àg}:£pƒÔŽ}ß·t’º¨ša¾—š¶.Ç\E¢^š I,1Ç(Ö!œ „> «îÛŸW}¿·¤&¡ôêû…¾½ÝYã: ˜ër£S¶W=% TÊŽW¹ðœÃõÑ$¥G²‡lÑÜ8Ãm‘“k—ÕŽË;Þù½Ík/0ŒáówdÊâ´oay##)ê£)ËÇ÷æ¦{ÄtŽ‰è±ÒŽ!‚¹óË%ûÚEïisÂ<¢á‘¯;éK2yÐówz• DBIÑU^г«P$ý^ŠÌu¤ªÍ#%1ÊáEó¯#$Ú2A†Z»5šæ1÷].×pËËu=o½¢o<{ë·¦8þZüGbòí¤5úûþ ?$z‡"+Kˆ…Ä®‡Š í¢ ŽÕ%‰˜#b,7ù÷gå²#$‚´a量q¶ý§×LÍÜ¢iêÅã·FUB#%ÕT颔à#)}ìö}<Þõ9Ä*<÷Ç‘-‡Ã7K® _‚(çÏœ#)nÑ«ŽzzŒlž™ÿg[Ñv·/ÃëiN8t»~̪—E.‰äÄ™3?¨°=rØÍ®ÿ+âÙ½Ÿ6àãì¶h仳ÄimKå¼\Ç´g?‡›Kiaàw»Íü5®g¼¯ã¿ÐÏ÷ÝðÀj³…»<öøÆ5ôÖ¦IBitàÿeè}®ëƒ%XêÂ/u5:ºÝÇïtéô]l»?<Ô <âðÝ€(Ò5–写Ž$’_¾áv“øyæ*¯åü•%<s ˆíón»#$êDÉ*1ï—ËçÌŠL€§Œ4ÖØ#%%ñÐå7våóeù~Ÿ8#X#ÌÉó£ÜÅJŠÇÊç {ÞÎr–VÜ„¬g³,qz¤_™ã[.ê1í­Ñ†¶±’Â8‚= ©EJ$ݱ!±Ã"ÂAUZPq¸TEŽš•BÅfŽîSFe™ªŽ#)˜*KhfLvÁ¤Øi’hÀd,‰¡¡º¤ŽÆƒMIc+ŽF1Àvéš4ÒJTD0))'φq8&¦dÔfˆHÛì‹B{qZ8dÚu¡ýß²ø[Ñã!xµœ¡AAóýÜÆy,?ˆ—”b—ñþ¡­G%¨½tr§D,;Œ,"Œm&;Ž˜xrE+ðøgOoóÇÑdÈ#psuÇ…¤ËGGJf³v…¤Þ ÆA±aR4š„!¬÷â Òm7&²2¨þ¥@% IšN?²ôöË]žü}×W.Ñ>œ¬çû-莃eŒüøÝ;1ŠE\+QÊj«6hW*0åXnÓK#%ߤã³0üú¼·ˆö"®Ï¯ö÷aŽAùÝ»n¤t=5#%'7ãœYtFl]&®tüÕ˜ÅÎóõ,©ª“‘º©ÛæñÇ?êüÔ=ó”auRhô‰$v»æ.ïµ*Xíäó~ï½}òñ͟Ø¢V„y#)ýnç#$2#)4ª×õm`+¬çøq¿ê…[vrsã[—iʹNΧ{‘ÅÎs=ÊêÕ†¼½VÃøôvǺ?_çšGlƒº¿½pOlöÌóùø|_df_O7÷×Ë×É.ºþ´N‹jþ}™¼ô˜!âü#8°:ýåö«Šb~ž°vM'!Û ü¾Pg¿V„2biºÑäjÑ"¶”ÁÊØ¥;䧥‚£Z!¡”}íæ¼^èóÏ9·Œk»µÊT’"‚!R¶¸aa‰F$7d™eÇ Š" Û£Ó,E…®¡…yjÆaYlïí¨ÐòÓ•·­Q¨È)j•ª!l¨Z7UPÒ؈ÉAºv“y»žL‘wä%r(*`¥2 ÜÁ”ilÄA4‘ƒ‡}^uWm’šÆ1Ò!³,ȘaÆaYÚ¨´‡Ä§qÑÑe-®g´8o™Ã!kAeBŒj0€0®ñD³O†ökQF†Æ&,‘&T8Tâ$z°1ø¨­Ù¤È#ûš#)–´ª¬šŠ¹£Pgj•DaWzeF8×Y t! Ô ¥\3Hf Ü|ªÃU¦jJÙ4Ryµæ›·œáëP¾i¼ßCÈöð,j©ú¾?î:¸8N‘ö2ÎqÕ£ùgGeîù¾Dê=#že ãš®[ý¶„öûþ†»Þ^ëpˆV©…qáý”>û3"°ø?½®ŸØ7ù´ripú0š Áë”SÈ1H#ÆõèdÚs”R¤E“¼Òw,ƒžÏ+Îgáy«_œ¾rËÈÄDQ率½ZYèá/>ª3xú1îpT²¿;œHW_ü,_£LC¢#))ˆ§‹”#%áyHâHr3?aN² q;¡ùêçÑÆ…"[ ÝFšCžFÔЃ%øºË™rPUI$³*ªµ}*>¨$uXK€rT‘©ÃÇgX€p‡qÜnCôÿ…Üùƒ™÷üx”„ÂÝ#Ð$no$ãºÛùôáƒçïý^¹&'UUP!ÿ¥›7wN!ÞÜøúgÛô×ËÜk}·P];ŒÑÞ\çxÈ}¡¹z;:™¶^/ônKaŽ×¦•;üf’&‘h¯°SHÚ>=ûßm+Ñ#$Îêà@°PÜüò¯ãøÔ.%e2Ð?hÇPª‹X¿ßŒ6ŒVYJ%QFH‹ÔK÷±[#$Ÿé˦ðNpèìJ€‚¢²±D`ÔÕåŸgóù0öù2¯×Ê9o[PnÀK~äÓóèh>•µ¤(`*‘b0Dg¬×6úÄ•Z,D“ñqðà±…)“ C-6ÑÁ­ÚkC+E5‚àf-jkta–4A„By©Y¦=Zê÷QVƒ!Zƒ#EÇ3ƒˆ\˜e¡A¤T`ÄÊ+Q`|ïCÑÅ¥â^¸˜X‡ªk1äqVÕ£¸ÖaÕ#UŽ¾®CfÇ&ØV5hnþr2¢0Õ‚åS¹•1é°2ï2ðlˆ«DÎÏ€fd²À]€æš¨¯oVŸ\!­VÜcmÄ2ÓS¶¡Ó†Æ¤ÅöV.Ëc4ÓhÄpÊõ;Ò­£G‚í3»;Ct8Lm‰ÖÐë¶X6 ‡Šn¬ÈdëÁœ4¡TqêŸÇþ/ôìÙ°å¬3å+¦û 0×®ö\.»¡}÷{Ñw­ Q'¶AájÛóº)⺙p<¹àÂ)X,5¹-6åù~ÍOÛúŠ›l hNN~¿ÅÂüÝ€v7}ñŽ–š‚HÍ™( #$äTbP/·)“M‡î¶ýñÒSL+_ÈrAƒ<cžÿñåãB`FÁôîŸ!ú1µ‚ášsO?#%¨÷äÔ7¨ŸA½:¥VkôkäÕçÀs§8'œ°@ëpZYž #%ÁèââåAe`mŠ°ÅƒSø¡UD q¤{.ðÇ·YŸZ5èÀÀâG¢.×^ÎDTêvº‘rŒ‰7Sk0w뱪ޒ'h¨6ušÃñw†{wyìbÚ°ñÖ`Ó{x9 GcÈE¢—HZ-ÒÑñ¨ÁÐ܈¶ÝÌ#%Îm]*«EP²§-¼ÿuÖí ¬dôq!å&€P””f4½ C#$^~mÿú¼þç§ÙËùíá)O5Ô»—¯!,ØuŽÄ÷^;<?¿ºŠzi`CàŠqU÷aø°Ðñeã‰4.ZP)‹uF67eÈÇs„Êæ ² NŒ)‹ƒ!oóÈlj00Ç;1âê˜Hyƒ#Ü$mêª2©Ô";-Â|c®áÞË~åÝyÛÝyst3‘hÇ#N/J·^Ê,°4'×æwÙ˜q¤lÑ ÉÅ›pzE°Ì)Q’f’™PƒA­0ãÅ+#$hg]´`ž<f–j†‹´‚¬-Ý#%þf‘•s+|`ª—ÈÚUèwHn yv#%Ë«w•#£¤ƒ‡ ŒÀÓ¨1ŒÛOµ]Ú |7"'kÓKF¡§­4ßGŒ#%=‘ŒžÑ9N-î"±–Å`j0tª<ŠL²F4GL„¢“V¿²kYýÖEºSÙÉ*“ÆÁDE*Œ;˜~W䥾hÌ÷4Ýív«ÎbmÓ|‡…¶X[†xÒ­A7íGãúý¼S»a¹Ù_ÀœP^/§KϦ’¨J8Ÿqc‰”xù ÁÒÎäñhv-ªÞÂëŽÂ+ûZx†ßY«oMFä™L[Á#óµqg#$:á3m¶ƒçboÈF–L¸G’/s¸3w1–ÛsÀËʨEFj®:çdÍ+TºK.sKqŒz±µZ$ÂÕ¸d‰¶õªÚÎ…ÝšY^ئfÍ{—†Ýô^=õx°v Ã{๠‹ã³K°ˆÒ†‘ή“bKøV|Õ^Ú¡›Û†·uú[I÷r8ê›Y«#)P<N=¦,Ü9v‘$aö5£Š¨2áBµÝ&ÙØÜFÚäØv<žD?mÓƒc3š2Öä…Dx¹Ðpk‚ø¿+ÞÃ|ùðãZ’O:u=\¹Ö,í¦píœâ7ŒÞ×OŽ\ãÒ,aÁ¨!µSyŸ£°q2!Cng¨ Éœš#%!& &HHc½±e êÆÕšú>s¶µŒ†0Ð:l.Ý;æ^“wNU²tÅêUo òµUÞLªƒ ·q+,OÃwå]‚'u×!ºÜÞ7,·äµÅÅ8,‡T/wµ=cwrîsÆÓ^K·n•Êˆû¤´Ül7Bˆò‚ŽŒÃs ½Ç~véûmGQ×dð “y‰d­é÷8Ø,æòÙÝ6øi].£VÓ!‰#%0á™ZÅKŽ‹ö\EfÇhM=ŒStÕÖ1˜ÓaL¡¥ŠåšÄÌ qª]:lÄç‹' Ól± Zs‚ó‘Q‡ÍôrMðЄ¸ÅVJévöÀøV¡Œ9sŒ’$ÚŒ°œ¦ËÌÐåá÷{ŽUãr¯·F+"¦ÉëòÖ`Ô¥AÊÅ„A¯¡P\‰{†œmí/晋Àsç[D(ˆŠ‡#ÄEã?NöØ÷dce¥ñ×c¥zÍã^b0â11¢ë,J–»ex¯õç§0ðLã$‘uK‘?&TÖ¹Æú«x̼[¿ñH\M^í¶ ’Ÿ™œUC¿uJ7+—êê"Öq¸*·¸ø+›y›žÅ‘ ¡ˆ‚_#%•sqy|Õ¬‰çj—ƉÅÚB•Ä#%œJ_8yÉòÚ\Ð’T+ŒÓS”VÊ’<\G“æ¨ÀTíç¦Lšéç¡ÍèÒºqÛÃ87Œ²@×¼pC’™u:ÿßgïÛw¾âÑpGJ–:·h¼'z»èoÕuœš|„'­'" Ì=ˆ›–sX¬m™ý\Ïó£[±žÓ4µ^#%q™ÑËm´ØÿI«<dÁFÏrciéß«|øÚQ×G §|í8·-Ü1.öû[¥HtÞŠeÉÐúFD˜út¯p¤“NñøÓ¾öÂ]•È×yÃéš4Éw s¯;Ì8o,³)±ïºÂŒ\25ùX·çómf½Ô#)Ï ·>LìÎdÂ1E×Á‡#%¦Žn£",U‡±íé]ÞTnöò(öóÞL‡Ãp«^ü^VŒ"Rò½¹7”$Sm­aªØ%1…#%QÜì1Gqøì:BŠE,í@ÂÆæ¡TE.¸‚^Û5k¨Üsvª{³¦²v=ã)ûŠÑ‡u¥ ÄéuØ55…,èÈÆRÀ9œ•F†–rü=‹Î;~v ŽBý×pq½grÅu¿¬¿ì/ŸñÍ c¢Ce?q©·“Éêi+°·ìJ²\<>Ó©T ,W–8¼þ(÷å§9Š(åìsµ,€âdñúaËÛç}@î{(aH¯Ô®¿ÑØZV¨ìÁÇG{æ®uů=çÉ­Û²f|¶x@Xè5èÉMUM›yp jqpdÇ] wsE"è/JœÁ<š®a„Ú2\Ìaˆ¶Ñ–|þ<;˜JG®ãÚ%u½bs  ˆQu嘅A[^I`QÙGï¶Î±›ùæÑM©eÁB…M‚vêp|A`£8#$@uæˆâº %L[šÄ|µ• Vµ Ãvø4-¹%}ƒŽÔU…=RÖ Ãy£”9y¢"0Õ€uß9m¯kxuËlÜ“Òi†NË}¤;̲Ùùéà¾8WyÅØÉŠS¤#%gY©±Q…ĽâÙ®LëÙérFtTUÎ]Uÿɨp2pñPó·EMç³u#):ð8Á2f*Ÿ)×Ï(~Ë<#%‘JJ·d²àŒ½ÈP‚¦ V¼Å6ì\&Ž3«Ñ®Ø$d[p”P{>cçãågcÉô³ë»ÃÎIˆ—îÁ ¶œ`æ(x;å?‘,V×Xñš‡v«é4„*HTÈ0,¢V#Ö.ͱ°§·Z‰îtÜ8,f½‚"ç&c‡TÚì ì òº:ìû(ÉÝRO¨´Z[wqi½?»‚£Öå»5äù(5Duo='v土3î‹$|8é:¥ÝD^¡{c(›„B$ƒœ@w½X¬ÌðÖMª<º&LF´+Ó¥qÆþAX‹‚zj½Y±G`qaÝÐî„™‚!ÆÆ ˜ïÎùÛ=tljÚFâÜKÇ™ƒ‰á‹ëòóîŽ%û‰éú"–M<¸3CŸ9ŒpÙ ^‹MdŸâž™gT>"†èîæ‚nÍ›·_j)|%ÒÇz6¸55øcOrúùt6Ï¡E'¦ÁŒC#$Œ·Q}Kj£ˆ+“Ÿb×\ÈÿË×àç›ÑCîÉ2ènCà‚Ç ü¢q´ð ü¹Ÿ|“!í¬7,š¢ËC8í‹PR”Ö†rK¿Óp2óÖùàx’Ñi‡C§ËSZÇŽ}ùovÝzhè¯B(†%·zæø5†1ÿ%Jg-†úš-¾…çˆÌµ´.Èòš¢žPä=Ž¿¿rÎÙ/ôr(óF4…º™åè~^ëoÔ&)9¥5 Áá‚x lÛŒEcÝ=\qá¹i^¼Pôh\Ý%µB[o^Ì–{-ÎõâbøDQ'êÃôc¯0[´ö]Á¾Ìž!¦7:|¨¢¾)ˆÇÀ×'Šá˜0c¢:wÞ›$ 3`˜£Å(…ºwÄW·&”ªdo¾?Ñ#éÇÕ¸»''/Ðôvðšøw.ÞaUN®—Žçä&ÏÌÔ}åÄo·¯qëª]Sñ†&^¦ýVWªQöc°0;·éqÃ}ž‘¬[šü'ZÔ#%#$½ÿסßRU¿ËÑó^T¢øô부Íi£Ù—Q4;·XrvH#´ÁˆŠÖFšýœç;ÇÌ|/<˜òë¦*„ ¶#$ø!ä|¥SŠ¦_ž¥;AÝeέ½ñuBñÑPi¸½\ª½°mEs=Æ"ë˜Ï£05ÂqŒÔ >"T³ÃÅõ´·Â×&‡Ë‡uiÚéé_œ}±ˆû(›ï:Á/¢LÖfž­.8r‰ˈÄ<2GÒ­?¿-¡7Ui«ƒhÓŒý[sú¦§w(ð¸±ç0s‡ÂÛò³×w%ÿ¡Ø¹vËê!Nle¾lÛ©j³¾;ÖO&²…R±“p)E¡ip…šdIÓXËm˜É×׳ç›—nþ$UÚED3 |;§m™yË{‘pD÷ó!«‹%xÛŸE«º5Â:oç'C†ÖÔ^!6x–ôŸr¹IöÛ¬6úC¿“¶s›ž™쥘#%.\¡"¦a¶ëÐA:M„=š=ɵ·86<kA(·77ž«©ŠîлOåÝÌk>^ÑçV¹óä¦ãWˆ7\u~¸ñeìî"Ë’ytÕŒþ…çÜ£ì˜ÎŒwÏ/W»ÌR†–Íúï²0Û>Æ™¶Ãµ®Qñv^mù‘F¬wzW¬ûXlY¦Ö’}E)} xÛ ˜læ0B;&? ËãS¦ÕI;±f¯c~—{l×c#%È?zQb…æɱ³„°¹ÁšõRLèد˜{ÆmS”+yÔ/<ÝVI×ÍÓÎÚôoäœöÛ#%°‘ãdÛ<Øk%HóÈ…pñÎó/À¼ÃöâÈx²ÄÄ.äè…·O§0ØõA†¹2a9ãjLÒ0<WíàÅ%Îþêý#%ëQ£TÙè¤sDS‰ø‡”¾-¨KìF3ld´Y¶Ù±¶õ6Þ·{HçìnÑV”ÙHN=½EÅEE4p;9Ý4øoO™$—¢?—ì;ßnâ]HëÎÓ£s¬õå-v˜}fe¢Ô" Õy,ØOãËÏR½±©ëNK'I<"ëΚC–¶OØ<UF)¾ª¨ðÛöÁI>…GM¢íÔ³# [mñ´9yb7µºþGôÙ‘]!gëvçŽÞ..élvŸ²pË”<ýÚõ\~NKa< W.FåyýUEæL׳œÙ∙łƉdî‰N‚:§'—gCEÜÎ=åóðÙã5¬è4ÏD1¡ßÓ@P`Î<ÞÄbf÷ ñà= olÙTgœéᩱ+¶Ó= $TÂø3Ñé.°›»´ý,©,jÉ®è"š{æÀºAr*„q‡Æ£»ïçs¥*”S9À ©ÏÿˆÓ{ÞBk•f«é¢¥æÇ­d(6•‹¦#( šKÍŒÝã…ó]6ÑU@Ò¤Á+\[ªq Æ;+Œ,­]š8ý³A•vÔ\¦wýÁ™Â¡ª­”#)eØ'p.z½‹,Ò¹ŽûÄ!˜¬ŒE.nMõ5j^Ö¸4K‡ì¤:œï’°¸¸KµëÑ™uP›Ù(ÿWï¥4Ó›Hv4зp~ü´‹a0†jªH¯­×©zìuòg Ê*MY¢]»,"á„h2[j `hŽ½æL,®s>:õ‰¹•[“t—<ï ‚±ÇæùëË¿»¼ž#%Ïpîù~8ú»yó¹™íã^.zaƒ‰’,s6¾Û¥|gí­ss†Z”iªºjÝk‹Åk¥ÔÒ/¹Ó–r¬/kDTsò>¹.Ðý– ¥–Ô7:Ü#X\Mt¼µ™Ê½æ4N”;U\œæ·¼õ·©&²Á˜hÜÖ;5ù†NtÖ8Á‚×[#ÜĹů.!Å)› ðûe·–øˆg¶ªäÚ¢ò"4=ß#%g¾vç>5SAöQ%#)Oƒ¹Îzù>ú½aNwÏ?âßúL þJ¨¥­”乬†úž²Ã70“êT¦sE3Ú wf¤š“%Z£¬ÔE›ø iˆ¥×Q¬I@u±|ž%hyogr¾{»uFr‹#)Á:€È W¦»DDØ,D¶åQ|[1eg#)V*º"÷ U^q˜ýÛù=<Š;Ùݵ‘öT.v|*a~96ÊT½ð­Å›¤ÁåÌsGD^ËÈM²Ön.ûî•Oº%1"]œ÷kµÓQ†O¾/ùp–0 Õ"Š¼m˜iø<£ˆyWã’µ$e¦KjuJ]^¢#3Ì›ã›6K#)¿Æ}Vgwyç—¿ofÉã½ï"Y¯š‰üª¼½üY? k‰—…à Ž Š›ÁÑv'ÓyÎÎÙüÔQÌæeÁ2D¢¡ÆœVŠ¬9­¦¸YËs€®H¥À²öëÂnÄ٠ͺJÒµ™ÇÐ'Âët74,®u/KsÑ#%½Fu'*÷hñXM„¸ÄÚ`ÿm7ç fÙÊ·ê#ma\:žÜhtÍ·|JÂDZ˲¶{„·¨`'½LŠÔ4Y5À-æS«mz¢…aiªÍ…Q`"tEþ¾°¿’ƒyÉÅQÙëãåyç5d‹K>sª÷í¬}›À$ˆ]6›Š[¶ÆnƒU[œEEîÇ93“BMiÆ>ìÔ㮸Þaq8§MN*Ç’‡&#%¼o}É(Ú<ÖÊ{ê!s«çÓW‹7Ӵ篮óÌkß$<?Mö/.v‡kgzÝq1¾¸ñ™¯dÚ-|©ÿfÇ—8®Ô:[{·¹ó®NnÇâòaæ*버Ë,ª‰P$±Ïž:oÙ”©UÙhõÛDÑȲÖL$IˆJwè¾N44†âêmðÜùmÎÐ'A½Ê¤ŠM_#)k>>=lÿ.»ãaÖ·'½dÕ•VÍðÉm‚0;#%vf|­ð©ZœûvŠ%…ÂàÂ{í|QÎ#ýö™.¯õ-§&?§Ï,›&—²>{œÕÕ;(ž‡så"Z-K“Œ-"èâÂúV“ºÅŸ0¡?zÁ¬Ý™¨êðÎZù%€ÓtÒµ|gKŒµÛ‹b2¡ËŒkÓ]9œ1ˆ}¶â‹ h$sÑCÄ#gj;i…%"ˈ,AzìÒ±,ûcÓMË„ñ•„¥$ °bn‡f{Û¬rïÔú:=6÷8áÄÐíCY˜¸ÅqÙ tɵÝ7 ¡˜y`Ϙf¢WêuuDU¶çŒó„šØN°ÊŠÚpôž%œ ÑiiÐîHµµ0Ó,ò}Y® æ›Ã†Â³u¶ó£(ç•#%3ÁÒÛm¦Ç»XÎϪõ±]Š¡J.Ì·›¡}ò}‹Î:u ‡S¼·™h#%“RÊç¹Á\ÃCÜ(·t\(…a<õg‚gì¤1šú”¨`:STÀKV!¢2Ê,²¾nhږ󪓖[ÔUøØÁ)ˆ^ŒTA<õ_×]ìçûïöè9üðµËÒûÔrö«Ùüî«Ãš––êîºÆ†ôs>3¸øÕÖ‡Ùå7¥\–ØvPòçñ½RGâ.åÛ¬ZWŒÙåN™<_ÕD 8 †zò¥î}B3é›ã®÷øÉ?%„R\}¤Ov¯z7æYÞåK»VÎÚºçT¿SÒÀ'EýptCd/ýL78—ç#%éœao¶õQtǺkút-u¯Wz°ÖÕ+­Øç:«8,j"#)mP³û"³ÝQ‰÷zÇ÷ŒÊp§j˜Ï0DÈ꺸àµw‰"±PÇé§!ŸÛß-7ìúQ§®"!0ÿm“Ãï~tÑ.¶3¾|›ki÷Î(Ͼ6àšÆ6ãâw³9N7÷00Žrœé~…W(•#$IoÀÚõ}¶™°Äå›&©J!J_OV­°½k†Ñö|Õqâ5Rý`´½DJUoë²°õ/*'¾%tóó³ÅçÌõÞE“º§ÌUG ¬»ðéòÎ<–FB­hhƒŽz½ÂØ­J™ÔEsNçrEÕã½Æ(H\I•ÎÓJŒôØ$ྑÏÁë„tQŒ¤zdóºe(/_e<ƒ7T#%úmÀí¶1‚¡/g÷›öj“;jRÔlÛ_ë¬ui¶‘i¨GB‡DS™ÚÞ¢çÂta-MMK 46ðÙN·ïŠ“)NnK µq…îÑÐÓ0Có®‘ØDD„1Ôùé~§PPËÐB¼¸#%r„oÉmiÀ+Å»EîEu˜NÊ•tGž;!|·˜¼h~W R b¢Þán‰)Š¦ëÚÄ™‡êFGÊEs\©T[…-Y!á@¤§aKNól©äuäˆRçóup|SaOdTj~Û\Ô¨˜¹›P‚ÓÊÚ.¹Ühßeu¶ÃZ.ç5]KÐÿØHv?õMùHf#$*˜IE#XÆ]qRªƒ­|)=Ej¥ÍZÛêÔsb?«Ïo%åƒ#)ÈwÊšœ#'QB4²®ù+NUB+–ü¼Æ™ .«xÍH¢›À—]s£ë£ókju5 ©›úŸ<çYç NƒF<¼¶ABñVÐ9´Ò§lÛk#$á´á%™{¥`9§Â§#)gBn¾€¬¼¥Êö÷ÈrÛ8UA”=ô D´DÀnpnQq{,0Xš£›ÂŠgYÛ3ч\ïäî{Ðë^‰Êòé=ù±@uï#%½™ß=¦÷râbhª¤¹²ÛýÇ÷óÛ8âÃêDsQSסϷ¿Ñøê%eþ åG;¯>nðþ]©ø­ÇÇytj]&uœ¥_ˆ­AÞ­jÝö_ê…~=`0: U/]’x8±Q®W­.øßµ„ºñhrÀ¶fp×z˜BÅϾ\£ß_/FµêËç²#oäqý´æ—Ã~9Ø¥øuæt%”´W±åvžê>"÷›eÒÍDTPG;‡¤öO›ÏÍ"wáG«ZüŽ'Òë}©³ø‘sï¨X…ÕR)åØ3ëüÅ:-#$œ™pW6C¿¡ÏD%#)sØÊÑÁÇw0ÈÍdT¯Æç$Sç†!’¢ óVáw:R—ËÚ 0JŠ#$ÁjÁ#%-RWH•hœ‡4Eb9³@tBžWg(é#$V¥T$¶•ã0¯ ‹heÅÖö::„UŽm#%I£T:ëj˹K»«·ˆõG!R9…*„À¡œši£“-ÊcTE!âôØ“Çݺë©NÆ!Ú¡pu¡\%¯2猶Ì|ƒ5=ø\Ô¥e¡€¤ ¬¹ÈžÞ²Œ+z-ŠQF#a%pð•M³GQW0upómV¡UhU×Ô„Œªçl}õ¤jÒ_"‹…}òñnŸå.{Á¶Ÿ×.}¾1<Y3×WÑwß*ûŸ¯<óºêløHš*{¯†rè.—±£tŠrœ˜¹¬5‘a¼Í%–'w©6SNQáÇFؽ¤m!Ñä¾’óÁ§Zn¼o×ÏS^~dc”wï4¤3"b)ÛúÑ=¨,Ãê®»j0&uû¨ºï^.ÓŽóñßPd—úmñÇ–,áøà›ßÅOŒærF^Ò&£Ås“6¦érÒ§Hp/@ia“GÆŠ¹Unh `RþÙ½‰±×«E Ô½ˆÞ´à©Ç]#% ó ”¬§ƒ¬šÒ¯Al ÂeGiÛ¸<³Õþ¸:©¦qãÖ'‡nn£vtŒâxªxÌQôõtcšÀj)¢mˆM»v U½ôš)ƒ`Öu#ŸG~û¿È’κ¹»‡€*Ç Ÿ£ ýWI¦ªKÈfQD‚T«X´wÂõöKnqBdn„¶ç²è@êÅëFOÁE”IîµgEÁÔTº4®vˆÎGÔ¼aR±Ùá¦è©5Oèů7=G¹2Ç“#)2ºVº =Ìè+”ha#)0fÏ—aD3bíVDE¡ÑkmðM~¾ŸœãW³Ž|QÄyÀm. ”‘ºQ#R¿VÕKÆBµ2*‰¡âì¶ÂÝ£Þ´] òë~ŠÏ“ÉÌøNo5))oçæwˆtàètp?Äú{ãÈÜÇ:ù[ˆï‘âÜè K—õE”Ÿ«ü,ž-zMªÙˆòò5teA#Ý@i÷?3齑+^Ý윒åTŽQb\#%`ñ€¿‘l6èfœÛ Çá„ôŽGóC¦Kâ6bÔR|˜>h‘°ÉÄÛ n–c°¶té‰~Ý&¡É^;’tÎû9¥•Œ²s];îI‰«WyžÿÑ6c”çh…ÃýŽ9èrjiÜ)á¥v¶¶#)Zd8^<ØÍW‚Ñ6%³œSø «rU»‚™“RÍ'šãÃðÞÜ@°xWºNLMü:žHíé¿nÕè€Û–9úyœ,&1¢¡@ùézötÅŒÎÞTðøWŸ#)5ÆŠYmA`#$uìLž@±†ôÖ-ÔM{ Ä£ èþýUÓ£•€P=Ùú²©j§U<¤뀩‹‹˜+fÁÕ‘›ÙÚùk^q¯§8x ™Á_MTVüô=ã#$'l)ó*Šœ±[õ=Ú|¶DgÉ=¡°t;}Ú2Ùx&ÆV´7ArøRz6{¬Aëô®Õ¥ž?µ Ö áÓLµŽ}#%€¹FËg<æRïf„îgÆ—ML:ñS:æ‚ A"¢çN¡[LJûQQÞ+J¤ðî@8Åz©ü“ö;bffüýžîX‰u42z+‚&KRF”ã?Ôæ}Ä–!\زVVQ¨{ž½£*y#$˜L!Q áceµpJpiM%Úx'¯}ßòoP§¢ƒºjO§b<cÄlÌËÄ,½>fä_({²©|ûÜä€@Ú»çóWWáÝÞžHŸ‹wMtl”¶¯Ní¥#$ #)ùHOIÁ? ~€{¹QŸG ëÞ>ìæómDû& MMËú|n¯B“{Â)Ó þnŸ§þF9–—ãl­…Ó%ýçdPûÿ¼¨qF ܶ$´ŸoZWñôTJERŠÂ,") ¡Eg¯0ñ`r1"B~œ¸^t?·äçdÂeÃ[Ò±ÞLyl'Ý|Ôøy•á!E`)$$ ¡ì!¦xXáØQÀÐ÷á™C*2¸ü|LG•;UñlõjÑúÓ͙ژlØ>Žé+ra*÷6²ˆ#8_ŽÎЬ( V>Otxadˆ—ì­³ 3n±Ç¦n¸‚B¨¡Y‚¶žÄø#$J*”C’B£IQ¹]éžßéâr=ÚJ(xþ5ãséÏ_õ2Þÿ•@/=tú×JÔÍh­(÷ ·Þ$ËCÀ¸ÀÁèO»‚¬oe.F!QáQxm\ûG#m@Ì«¢·Ò>#$ÙµëÀj?›#)þÿO¯ån—B¥<Ë´˜fÈj… ÀÒ’çåIé]Rz`àâ–*€*_‡ÉrGù©”ǼüÛ™Ás#)EVƒ¹E|vÛ€÷ã&Dý¯Ú¦‡÷TC„ºI¯W#${-þ7 ;Š?!cæsŸÙ¿Ã¡î4œß]p¬Ð!T¬Ëp˜Lû­Ùó¯Õ£âû‰W3Áq*ЃtT•¢T²;Ÿ[•#$+~áª;òÔQüa¥… nzŒÅmµ«Yn˜Š9–¢¦‹0Ïþtw:Dzl¥€F­EýñžÍ)Ѐé2P±Œ(áE5@röÓº„rˆ}³Ûì…áQ#%–÷Y#»çþ›k‚‡IDEOFÊ;çlC¬…²‹L|¢Èa_+™¨->,¤#%ÎÃ$È“ƒ’jPmHÎt‰@íа¥Ä*i¢Pz¢ìóPñD2€lý<õïÚbÛkî'ó@*‘¢úŽ ×†ÛC*¨o¹{@ð«îqÉ=¡ećÇÁíŒ#ªcxù¨_#$¯ái¦»ˆaÑ$l`R&5¹z'Ù![H‹á¶÷žžèrQš2ý8Y&ßçvˆçÄC³i˜‘ETž£¼ÈR#òdü^Ÿ#)`!±lœ÷™•˜ÒqÒÂÃìU¿,;iùUÎÿ•á›µ ’éÒYßò·ƒãv÷ÕŸµÝ‹p“B ´#$þÞ€ÿ^v†t‹å»õQÙË=æ Ÿwím¸P¯z¹ï%0F,8="å­¬Åà0B#%AÞS¸‡8éý—BÊŽÚ•›:¨žÊðwC|” P´fÄTØ¿SsWxË2Š5(ööQ—iå¢V@vB@Rª` :\vñ·E‰>0qVŽ«fN'g~>ɔŚÄ½Ãi¤+ 4AM÷ßò¹¶ *r&lÃwN´5jdHÀ™ÒâçpL¡Œdáš›È(ð…îŸ ü2u…ñμ»Žf%…ØÁ E«0ŸT#žmÕå¯Nz^¯)¤„}â#$§T+q_”À¢ÉBd5š¨#$'£ÛÌÁã|Ð%5ü®ü@˜Åå$í»yë÷»–n %µÉkÕï¿€$ØS"€ùœ?–IMȯ ã @c®™äšÄ…A†´#%ÛDhækŽàf@bT·U˜”œ•wlÒ¦E’²™F¤Þúä/k<»/ž/_ ’|P8WÈÓ&=,Ñ›»0E“QˆÈ¤ä™!C*S'2‘?0½å¾P<õU¦¾\É<¹JŠO/B9Áã©È®²›>6H;ꄲqêôåáÃ#oá¸ÚO Þ|4w~ž-ÄÆ!甩Ù8ôp/¥ÄFÜ–'|#$ÅÁÊŠdSC–˜Zöny¾À:cNEñS“—¨âŽ¾|0d1×èPÚùf>§‘;ô^;zO(0äîadc®W¸ ¢&#)ŒÈç¸#)MNøàé-r©N3pÍ!lHåbê#ü:;†W€ò3 \ä ¡­³[…¦Œã0¾‡¤ƒ±Åò&º¸Hæï¥-ñ±ö+ŒÈ®¶›û+èbq¸ÈiLµ6y–ûʬKka&I‹ü™ÆIB%¬`+ncXÃt‡ÔîáˆíÅ5·AÏyÛ,aNrœJÝØRø}Ž7NG~Ôqaq/8!Õ„åýQã¦s¿–ôÆ ¢:Ó‡Ï"æ[ÏŒã¿LŒ•ƒ²SÓ}/e{M£pß1èÝCïànPIßãËAØäŸgÛv }؇?O—I$¯/¿¤¿ºýqîë¹F«Ù‚¤îTj¾„˱–#‘6¹ƒØÌýèßc=½ñoU±¾l/ë)¦@ àPC¾G¶oÀχ:r˜Ö¾[xäü%~j2 RôÞ0ª35¬Ži¿Âሊ¹\¡–ôUºÎgP”ŸÏ".-©u¿#)l1£¸AÀIB&Ú¢Œ˜\ql\¢Ø {øÄ#%øpa²šÒµ íc-„ºƒî¨ù÷‡Œç%g¼ çugÒ¹¶ÞåaÎc)€G¢¡g&þ#)q\ƒA6*âÊì°ëü±`½ÓæâµívÜ_ ´ùh²ÄÜRSh‰i2+»–šñOÐþÊW³˜î?Hs\xâðvÑ•ËýPÄöv2&/Ýpe0T»¯Ïs#j¬)C‚[Ø $¡#)Þ:b¤\CÍidë;ÈЪa,Ú8­ïÂÅ*±¥P:¡=uÎÕò0Ó!-®øvAÍc5 Õ¾>{\Gàp­©ˆîø~äÐÁ#)8éžîo,|QÚÀèÛq"x(y§¤(´¦øËåJ®‚ôîׂ@¿È™ÛGï¾ÿkß#2CÛ§Ñ}â±Â´ß,Ç7`¶ÂU³@ÞÔÛkÆ¡Žf­W]³åEAuÖ#²ù<ó×N˜g}vØ!ÓÛÑò”ÖFÅ}Ï xL‹±2mýÙÛÒ²{‡ʤ—¤¿ÑÃÙ$‡¼à~;GnÉñÕùš6‘­§Úº)ô½âï›s9a(0.#)ªF“aó.ÐST_Ž"p±ùˆØZˆÎÞéìÉ2ΣÌƾKdJå!ØѹíT¹ø|1<=²s¦>9O{55ˆÕy¦D¦|õñ=ž ê®G¡i—3ÂfzÄÝB…JªO4bÑ®>m÷eWkZC9š÷BhBöT¹^þýùô9ù°:öãso‹A—f/-@‘‡;·‰á5tw3ËùrXn€²’„Kî×é0I².Š­–Ê,ÐZÅV‰¥áEáÅ5XìöL$·/Þû±[Ô·§íŸ6ýå‡ØûÒ&5P9êÞóŸ¶;xÅë¹P±§ËAgÙ‰äpδm¨Ò5IÃ[{ylè=ôVòcÛ¨¡¾5{#)JÅMZQi"Óc#)m9÷kòï9Êa«:ò7æq<xJÒ€Vx(p¨£ÐR  ½FÛ€H1 º0ýÛ…Y€ã¿p…Š‡›ë«´Ñæ3· [¾æêñ 0×Дþù#)âHŒêZ½ß®o^Ηíóžô„›÷‹þ¦³Ó»åì箑T ?ée¿æJa‰Ü"¥6»._¥;v¯<ò¼ŒQ¤•XÓþ_÷½ÉÞµM3Å?|·ô±™xøŸ¶Q5OÚ?›$ÈB"Wáçð«á ‡…~  ýñ} ó£Ë‰æÆpè>¯g˜0÷Â{ñõéìÈ‹V”ŸÐ‡bŸ¤€¸?ª@ÿIüŸÈù¸ÿ¸Íü¿w×ä?áÆžÉ)nx)§%@‚—À rÖ#)G˜#)|z4ï(M8Rë×–»÷Ü*;Ž%9ÆéóĤ=9Øꂹn4\I­Tœ§A nM«d³ðáérc£Ç7!ßêÖÄø× ˆœ!¯ª\¿…Pc#)MìO›æKò1¥óú(w±4°èchlH¼±Eî!Ûqm‡c~þBD†„Ë#) $8 ƒ>ƒLìÛöQ¥ú[ò?ÇÎÜa½¨Öˆ 8òŸ¤¶Fø`c9îíµ¦Á~òç­ 84—ïêYUß‚;h ˆ X#$Qø#%²;v°bú" 9¢‹ìîühÜ4ªßãR§,<ØŸOä°éÊ¡«ã¨Ý™I:nBÖ#$È¿`ìzmë$†Ç½“Ä}œù´¿¼ØH<JaE¸ÜCòÏéȬüz~øîî©D¾˜-¦Úš îÿt7‹í"xaK†Pô V%Ad f.³±@°äƒ”BÌ=lTL`‡TG¾ÆÅ9QdBÜÀ‹Jk)€_@.`˜#%[)`?lsóÊE¸dš–KREºš\¤t¹AdŒ3(hK©T&Cžë vâÐó¥6 ÂŒ¶ ´\ìdV; FÂF…L0R,\d©ˆŸ#%ÑA â¯ô¡BÛ=‡|~Ÿ?R©þ›s÷]w¡PW†æ@3A€¼°%ÌÁ€re¥Êì÷ÒøE%Ü#$`é¿`|äy¨™¯CSÊQሩkÛWe ÙâQ~äÀgÍ>Ñú/Ÿ½ýÍOs0¡=–˜È̆ËI31áãz¢tí">µòÆ#Ï1Þt®³}î©ìAâÿ|›g:ËNšàÝÃ9#$$Øn”r÷Y<tÔ'izOì2p€‰‚ö#)¢#X¥2Q*¬AbÝòmœ\"&óOe\š¤“#)'nÑQj¦' ^#˜qš²(¤© Q“çU1L-NA÷acàxÕ¦É]RÎ#)8—5j#Âq«1>Äìœ'w˜Ô djI§§íjÈŽÎOhê¨ÕeSþ7‡hø´ÅÙ’¼.ôyí6z­Ë¶Źèì,ô©»›à+#4åž¾xãã;àc¼þ™±M‘ëq¶–À5>Û94/`x›º¼Ä’Ÿž#^–x¹# žEôõ’Q¡E"°R1HŠETi4²Íi34£õ~ó߇â1ÈÑUT ,¢ªvëëÖd8̹ÌîL¦›M¶hîunã‚ç‘k+´f.è2#±¤fpĈĘ›}äÉ 8ðÂì(vG°ìÖ—3¥žß;?«Né“|[XÔ”ï Â#)B)Q6ì7Tmq2çKž€sß#)á¹Wu¨ЛÜ]Bì0†”èe†DòÚ_1r×› õ˜ ˆÍñAÂÐÃÄ>ñ„ &Ø4؛Ø`|ÓÌ}Q(ÆΆ´pØQª%¢¦Þƒ¯Ïmq²}•|jºLÕSvÉÄ7ÂKšã[ÈÓ¯„ ¸8†'‰éxÂCÉWr=‘ ¢–#%‚iXìwXýÙÁ]¼,qø)$#Èzéc# ðœS¹<Ñït²'ö(v2†I#)×Ѽmµò×5²V±­,¶’­ŠµdÖÞ*ºZ¯l:ïÈó»ßuÚ«ìûÝ6JJH´Ô…RØRŒEvÔ9 svlðƒ¨IáÛAIBØzé­ÇP3'K̂ѻɛõÄy8R)j4Š¸~f|¼>_\ži5<û“s?|y@£¥}n‡½hð¹J|&/³Æ¡ÌÓº|¼lY'4‚0U"$"€,ˆÀŠ”&Á´—ÇÀ¼ï’™«xÔÝÕˆ`ftË2/Yݘ%žÂ7¦¬ jsÆðdÈ#œ†Òð"Ú¤¥”™$‘–óÅ:aÑ y»Ššä·úVº™skï±AyDaÅrË+¡ŒI#)ÛF¤4A¼ #% *˜¥D"H„"ȵs·íC"¨‚P‚Á* J‡mîæNcôãDïÒŽ“¯@0v‚¢-!TVšôÄ"1BɵOl–0îfàʸ#fTâo¦z÷_ÂbúñF1"‡‚Œ¡6Hj‚"aN>»wC\ú6×d=}]”®z<Œ…tåÕoÅ‹1¨”Xy#%KI`ɲ‚ G›eùmã˜=‡G Ú#$›iÑßÄ’<’ø”IÛ;yHsT*u×UášÊXq¢È°ƒI#)wó£¥Y¿ÄË"ô5$@›š¢»£–w@Nɉ¿f¾˜ì¬KBC†æ#%ïÃF¤ ó=xË# å£æ¯¬{–Òú7øŽ}Ð8Z’4ªñ'¹‚ˆòB§ÐÆ,h¥!@¸O{ɹÓ5]Ý(–‚èTÅ¢…#$iß5Î@ÒŸ&ó9”b°f„‡¸JÇ%î=Á×ãæÅl~fÐÜ)¦Ä}Üjq¤¡'°d(Í´'ƒä©¢Wʤ”D@É‘0Ë,‡EÞ@¡:{|ÑÕ ÀÐæqÔª¥ÆéËc¿yæ¹’ÜÏ 5XY}V´ö˜)¹xZzwwy `6eÙ „Ù”Þ¡þ·Îl+¯eªñßñicŒ‡;%™¾// aŸIA¡½#%3Ÿ·×ÄÑèæ@0‚¬21¶3Q/uÔ‘ðï|wÔz^îÝöõ±§sá¼4=¼Bá÷D¨ûzÕÓ­Bá“…“·ÍÎó$œ>7¦üNU<¦Ô$À,&øgâ†a¼ygѾŠƒs,³#$q ‚`ÛÏC.ë•q—»K›ÓKUB¸ [£ušÅM4£Cn_‹ÎÌWœ¿Œ¦7|Xê> ‹Ä>'ÛÑß;æ×¹uH†‡[§Ï$:úÞì#)D8ëÛ݉ƉgK•\!S£›ÒÚ 7E÷vã»·4€ªKFˆB‰- ZTK=H@#)AbHéâk#$•5¢Œ¦îi9ešÑÅÌ`ˆ¨Q@œò|iè‰Ò¢¥Ój ‚°pMzvÂ4Þ§žLhÑX·J(É:C«ˆCk…YÎ_"Ë)Ȩ#)M蔨#Ý9î,ó]»íYôÖñ{±zÍÔR´9ì®ã¾o|u—ÉÔÛ²Í;§Xök)l¢Ís™ö¾‡¾ÒÆ»úàÖñÜs¾·²:£!Fê bÎPÖxÜ’âw=E†fü0Ô÷ ØTVU;@éÝ‹ìÐ_n’ˆux#͆vØ)š•PÈ·£Ñ}uò>dÞé­rœ´ªŸ™I}q^2úqä¡Kg«}'Ð% pÜ&ƒ!^#$sVE"1rœN”³q2M’»h¨œ>°ŠE=zê),.Ôœ+u˜enGpaÀÝEÉÎ ši#)öûÿŽÎ¹Ë@Ölª"r£ Q"BŒ-a3‡–}%î¸Æç’CÀv|ª•JÈM„?3'³µ{yé¹b+ê¨roå¨åZ$kìÃüãSÙ¾(l.o¡6Èîûìn<4”ç] ìS.³¶å,ÜsL“M£ä6H#%:EÓû¿LØ™çÓZ©#­i!Ñ:®Î!y¡®ÌÙׇÂò׿Y¨†ü{Ã1)1­„Vyê;KZE F¶/†o¶g#$e‰¸Çlî 6Æ;?§¨ìI•àÜFÞDx?×Âë´(ã¥@Q‚ !Œ]‘7N§’47k'•¡•k¥^÷ÖãœSaÝFq8æj\Ø@I¡TWKH"Æ&‹Ië]TšL˜™§­#%UŠC ©Á£FÛ)j…ÆMqbißSsÚs-HsÁÄ©r#)ªkD¤Sp‹'Òhû¦ŒCÕ½7n’ëóì:go8™g×.°<Qñ© Í‘`¢šŠ|*x,ƒÚ$õ%=õ_œq É»#)ñÁÕ¶@8™¤ð…w@Ýß=¾æ?Å_#$ÀïêØYÜ[^› Þ?pщ*çÁiKûŸ …®L à–?@w?gÙŸ2“B“ü¯Ãyìο,ñaèÈ¡4y#%^‰g¾¾A™¨@#)|¢€ÃáÃ+¿m#$O¥FvÁÇ"`>øV%ãóÞ‡T•¤+Bé]u{*½ä³Ãôþ9†F4O•‰ôï×í©â#$˜â#$›^»/„2Š\xÑn^ ×{ï#)qªÈšDÃï¦á.ÏÉú@qr¢z˜zñOÏIPæa‰íß¿ç˜c€o¨ÝÚ'³¨åá×xÊJ‚nÿ¥øâX¶³OÅF5ÇÔ€˜œú#-)0Ûß5òš $Á!˜}\ž¿¯÷}Жüó á0¶(¢¿¯÷ŽÎA‚fd€-RtŠù?ôÿƒÞüA9ÛAߢ=ϦÿlÔ®¯FUýGµ¯/ò¢0ìzÅŽH¼¿VaÚËt.»2‡yòˆh€€ú—¡Q[^Q»‡àß+·Ž[z¯o Ezñ®¹DE‡ôÿ“üÿwÓýæþoéüÙ?åŒ9ÿb_ýò곬ñŸe#)¹xÚ]÷ÌÕáÍõþÄÿnÚÛ#Û]ö«ÛY}ol*O_—ÅëOÞ‹Š?ó€$f'Þ{ìw¿Zœ€1SLã(}kô>c’[glæè šýÖlaÚÚôÍìç?—ßô¥¡JZ„à’{Áýÿק)ïÉÊ)ÓέoïØmÜ’d0á ›Ÿ§ÃË¿•EôGÇ^8ÓOçaÉ…5·÷Ë–K%Ræ¨ûUÊþKæO#)#%ÆFþ?Â{ŒÆ¿°¹«÷sp_¹ˆî5ÌŸÝQýžyû¿ˆëﵸo›»ev¹ç€´HŸËAÝtlÚ¬¨veclÿ!4eZêE[m¦]2‚sLž<‚ öžq¨.`@V’R‚⣎ñŸšÜ3×,œŸ_ü­Æ¯üN&³Ù°[ŒÛ‘ub¨ÕÒ:V­¢¸Â©ÙÝ;Õº)²6Qu6þ!ÂÚ^„†ç‡fbPq™Ï®ÖÍ”ÆÚD6zíuñ˜#)ºÊqÓcþ¢-2ëó=s³×eŠË<jü-v3°J#);¥_ƺ/ˆ÷ ”åE#Y}ÒϯÙóãôºž”GâU)n1ðåøIÙ÷ÔÛöÜÓ_rãce#5]uŸ‰¯è¬ ÅÛÈ­K¶ä÷<—ŒöýK™ØE]‚ #)ID~Ÿ‹ÂÃ׶ ~Ói³/¤Çý0úãáXcüPÊÔ±ˆ‰,‡ûÇ.<ýú~‹èÕWƒ|¹ÑÇ„œ6ƒ†lXS (û…p¯Ç›”Yòy6UÓú Z1Åm™`7'Έ#$ˆ(e#yÚáÃÉÜ6oÈp€#$‡“ß±ƒ£ðÞKìî¯õþ-´Î^j+a¬yαm?.¹Ÿœc§ù½+wÜ(LÌÁÓÏìZ[»žIÿ㻓ê©H"žPƒª#%£—p€N^=•È­êDB¹Ör…sZ;~¿Ø½Ù=ÿ±$…lp%þŽú=!€ý¬zV×è‹péâCsr¹Ï¤,rãäÓñáIââ{¹JkråhòÓôÓ×(Ž}`Mù†|†YûÀ#%v†ît_÷лPcñ_˜¡#”~¯ýü^Žýuˆ|Nh¯NÝÓòÓ\{Éþ2=+{âÒº°äùƒRr¸cl sNÿyùñGçÏŽWìóøOðwŒG߈ê¥|Qñ„~ÏÌn¥Ø`6ØÁm3ú­ý~m#D$•Ã›?Pïòù“B(xäñÿFã£C.„*ê¡k=wȹÕÑl´/ÙÎÇ”ë‡õé@{;ŠÔþÒñ—»ÇN¬ò±O>ÍÖlè}<Û™¨­w=7ð";Þ¾Š.Ÿ‰×i¤¤GEBø>"œåò`m‡s´éÂLÉ #$Qê´ÁÞ¿“…·¨Gvþ\¯A¥=’`–„ d¢yUzœCŠäæŠÅýDô#ÕXÚüJ†B¤'s…êfqUüœo¾Þ}“ºµd"Èr*”̹Î'.KK3‘G)žEœ&Ój#$@W•ÑȪτÈA”Ùo$\ÑrÁ34Žpê2FñŽÀÇ__‰‡Uò%Aª¤I©#)#)"u±#$¢ºKj"ÊË+:ª°z»}ˆ¸ˆ"ç‘îï¨EÄO=™?DÊÀIË#¯@#%Û…{òAÒ ”óHßC’‰&hÒ\> –ü¨ˆ€}à >¤݆ ÀwŒÜkÃw‡÷Ô@¡ùôa¾æ—°?§\ [­¹—áùV$0ü蛬ñƒØØ+÷c[t¨Ü>1½O.¦Ó "s¶^ ‡¼Ê_•°ÀO•GÇâÆ¡ƒ\jw€ÜŸîï|H Àý=|½Ž¹ü€®Gíí>|ß‘Û…i¿/òwüu+ìíëÜ¡ 'Dyy~eñB7\ýü;-åÐ9ðv—5[ujŠ Ž¼¾bJr Õ;‡'àQ6iB#¶O+¯Úì îg~ŸIHúVí­üsüA‡…L#$¨Í#)~ÇH€YÇí‚zëaÛ$P…ªæ‡¿é[è¢ÙÏb»ÍhcDr‡ ÒlŽŸ·ö+êAPZ  ˆ¨ZÅ¡Öëz৅«$uIŸ7*VÏG­ü¯ úåï.ä^’úGùk#ùs=$¡.¾ëk'¥ü ¡pNÛ̧Tï­?÷¥ùÐæ AO;‹Ûßï­BîG÷g˜Ö¿…mx[¿ÚÓÌÖ^ëÂaPáá­L*w§«ÃÅàϲ®×{x{U´As\C˜tìÌ8ëÓ°ú3W<—?Òøþi ¶æÆõ„DUÕÙüíÅËT"ðô'{·_º#)•IQ'J:¯MlsÅ^醰ù·pˆy34u½v(jPq•T_>gÉÿfˆuæîÈž1÷Dçä\ÄãÖ³.Œèê^ÔNœú£mIþ×ͱ$Ÿý™ƒç­ñ'U碡ñó‚û (¯¼ñ‹ëÍ~5U:èu劾‡€¦w¾¾Xuyyo·˜¨ÑÚ¥6ÞõŸ¾ªÇ8sï·Ø añ»³­¶‘i RW–FW´ÏÐå«Å„Z±ç–j®¢÷ct/Êá•:X‡÷©+U/¤Î-Þ¦MG¯7F‹îD#b]lì_º}qV;ÔöÆhñÎÐt·…¾Mç^{Íඌ˜bµdÛÞQþLÊ÷ë´tÛb¾/Ÿsºj0AÆ ®+M¼¸£”é´_PWMîªÝ:tçñDË?~Ä>1^ÕÖOWJª(ž›S'Þü“šÆf.VÐO‹¢vïsγÓ:ñZSAh?dfœJb.4S¸Ã†›³ùœOÚúÆúý¹bÃ(:s)0RôI¼ò¿#%yêjª«³Ñ½ùæÕ·BÌ;{ûÀyÕ{#ÍæjŒ†ä&7ô çÏ_nÅÅ¢Ñ]ErV€S’¥£I#)^ûÙrÕŸºŠëš[Kó—‚F~Kû<¦Í†É°…û5«ª¾úVžAÀlËóÖd!SLÒk=£èvÔ|Ç ‰t#R~©ÙEé÷ÃMQ˜’.á]"EûÍ'?^GpfóÛh„-ÙÌv÷'b*–Ÿ.:üñô‰ÌÄ<\K.+ŸªO²Ù×é…Š;6oÏ}Íú¹s2‡x7ÏÅÖq Aî!àÐÛÖÓN¸ßzÛ8í3‘ÆéN¹pŠáMO2Oã6]þâœá§î›œ˜¸ª•#)Mê¤ÆôlÐòQ*€ïo”üY°}Ú;´*ÐV ÕÏaãù¶½·¹8mÝúÏ«¯âÌCÎ2ñÍeµŒ+9r úÞ#)—ãþ4Ç‘A¹Š§Ì86O„o•(Y¼“Ð%Ì–ê‹@+ïÉûÌÉÒ°n7Vª.Ï^öIßSG…Ì$‰A°Bn~m¡ZØRC3)GñÆYÂ˺{曡¡–»óMKýÜX°æÈ8ãéé#%¶ÎËHe[¼‰SËýH…%"슕ô¨G λìUµü£Øf¹ëõÙp²{}³1µä¼ÎƒKZy3^åÎêÖ"âXò2ü¾Gôűôó[x'ýq#)Gër>6®q†õŒEfXÄJÚ=;³ŸàÚ‚ûká-¿Òü£aÖ%϶_Ö߶+¼äâ«ÆB¯„rMù[]¹4íÔû\uëão»½ÐoÚEþ½Á¯Î×%TdZ@Vàõi¦LŒ˜eƒËõ Ü ø½`€8>¼Xï`·b ÑÕ61›YŸÐ÷H ºššÓÞ7õz°Ç»“òtmÓNubÌ6þ="ê3'•’–¶üJ¿?qü'ÛŽ¦S¡Z–0«+îùÔògëu}âe6R_™â`ó6Å™¢àˆïÀ„Ð{Ã/’cõÑà@–L}=6ùã #)H6œÔŽ8{ù·„Žµõ{~pÁ}¹ü.“› ö²¨“˵ì£AÙ„¤¤Ñ÷¡gÒ¸3ša¤ÙÁ0WÁ¥åO£ãŽ%×ÐL´óòüŠéÕÛNÎ?Re(éò‚tâaâ#%·Zô˜åî<;#$«H=É ü·«M´ Rƒw+ ÑaJö0£ˆPڸ朒zƒÊÀïí†#%xybXÌ|~ožá¶ŒÉ÷U„éå¿åPÔ˜ø è6Ogyþ/œ×J‰ÑOñ ÷Axr[ùg5¨ù(龺'C~·˜é øLŸÆ¬ó_^#)óÖÓ²#%©ÙŠÆŸQ,iLКvq×8™gçBzÓËJ†Œ^ç‡ÂŠß‡×&SÖÃGÍ«ÆB6ˆWÓˆ½É÷ØÈl°n~·õ )ÜÉ.òSÁü›;÷JmuÜ©VÅ@ÎF*¨› ™ÕUþ`Î×âIŒ´ Ÿ#)ãaÝJN îaÕ—£‹9£Ê¡È¡NŒ)(}Òv8a4º'¢‚ƒ#$ÔVÒ¤Ýg—i[ü7ôcæ <¤}»-ˆáæEºÆ­ß–!©ö{eÕ´zã´± fA (ÒŸt’Iz”«$¬T#$)‡Š¼aŒZ¢Â—€ãr?t*(¹Õa0ú¥'I#$ 2 @‹e5r’È3ùÜÒª!Pòy%7Ó³¥ô:‘­YÑö®E‡½ÝðV+Ó›{Mó¥ø—Lw³-‹™†qÑvyD©†rõ­ë#%;‡z?4צ˜ÅC­Í3µC逎9c›÷}cÏßu:Ãú’ízÐör/º„!vr¾éˆ»¶`è!†wcõ6}Þ¯ÞBLM|¸ 'ϹÜhAûôŒd;cù.ÐXËüÜS‡†E‘aÀ–âK§î ºX‚Î>4<~Fê”W_È§æ ‡²šÂö´#)ˆ~â'ΪÇqOèÂè8g  ò[˜w zõmý¡Ñp% p!£~‹; *ŠÖ@ßE“Ô3ás©.’#$Æ¥gþí_þ¡9€ÂšÈj„>U÷g}T°¿­ï}ŸçÕ m¬è`ÇÉùÝ1P<+Ž(o¡!ˆýFtNb)Gúhp²ßà>ú#$¾ÿPdÞó[9@jÕÌ Rˆþ fWAâ¾?ëDÏÇøÉ£øEº#$OG •©§¼_}•|nؽ#%ÇSÒפHýîòǘjîW>¿ÙöscÉè)$¿”äå`·æÌ<‘FûNwŠ]Cá<eŸ¦©P®æ#)‘ RQ-ðAÑîBÇì-ˆDßAKQázÓn©cÛ¶žŠã÷GÏ‘$ò*Á‘ƒõüGíCéçõË~žÞ.A˜÷ö¿âX¦ÿ™|q€™ŸŒßòéIaÔö?ÂN˳àüÅÄ° ’s l·Øõg}KÒ§`ìûój³Hòb |„HcÄól@À|eF$[EÅÞ÷=¦|¸VÉÃ̸“ðr"H‚#%ŸE²óSžòáxäƒÛf‹—•FŽ+mu¬>`´&ú—"ƒ˜~ÌGÄõýÚ®ªêwfÄ8§ÁÜÁ5(jðTÛÔ3”×,þNN!J`BUF#Ö,¯ L’œÄHR¨)¦¼0¢ªCI»¾¿·ñÝõpÃdÓ»aí9úÌa0BD $O4lÚ)PPíîúÞ)¨E0ð¿,èý©ìåÔšÃÒ?f`¿„Á<ø8鎃»ˆÜÇÓ\óžx_Ë «áó;y]6DÝHvSdH;åî’H‘ EÆ^ƒ¶/2K #ŒDNZ²/X¼®tæQ& ýòä#$=0œMïžòkðÑ@¥6ÔÇ¥ã¥û­ÍÁ#%¿¨kK^ýÞ“V_ó`wÊ~ج»…h#$³u):ˆÜ3o¼M„–`Mtv=ÑÚ”ËLæÕ½#%g”©ØõdäÖÜy¯ž“VÄØRŒmʪç17F~ŸPibÎo{T;ðÜ7LÛ,®&»©1Øfe7ÖqÕb·Þ`wbç×äÐS:+ǵ8í\RÇ`i>Û¤94©¨ûA=}ø0­½üo;u˜èà ƒ¸§Aùm”P•'@î}\ò¢¦Qˆ€^-ñƒB@Lî½PfĤÀ³e!#%œ† ˜¦ ¹‘)øbñISBò¥YøÒúG¥›…{G>±¬.´ Áè4aëã£~í¥aµï†¿)Q@Ñ(P:©”©mA ešWå‰åmc5 Kù—Ψ¡#¾#)½½_‡›ý,ôe3Z ×Dæ‡WÅ ³Ìs½réµ…„=o?Ù¯hmWÓ÷ã{¤üt|Wƒàøf!Á¼Xp=\k©Â‚š¹–³õ}«Yˆí5ŽK_þnÿXóhÐSÚµÿ(xúà§þoñËÅN„’WŽµWídJl/^•ÀòŒ 9ðÞ~%V¹ÔýØÉô¶,¿ÍÕg€AeLD®RDd{$!c“•SÌ\8t¤#%¯žàø”#).$¯­Ã½ÒÆ8@Va[…©E¬û1KšnùŽyôÛ§îG9ÈoÉrŽªÍÝ$W®ªU'I2gt©/ݶ‘³kâœâ Ùz6,•4éœ!Ufœ¡Š$ë{žû<_ã•)gtoâ´C-žnÁç¿)d¸`(ä.¢V¸ñ(#‹¦1Õ³ÛD0+^7½'k…lò`ŒdÀðV¨jÇŠ&døý—Ëe’ÜñÚŽón¸\#)¯Þ|ï¼hàn#%+yÓR³:ÄÄ÷N'zŠP £sU‰–=QglZEfØ pýê/©þ¡È0 ø#)â‚Ìÿ0æ¢z"Ôæ2Áe¥™ìêVº" dÐhŒ‰Q’i½vhg*ëa®‘±²¶89¨¥¤óˆ„¯O¯4fÓ•†2茿clòÓßÙ‚2¸Úáû;'¯"ÿ‘+¼“Ô.’mëXÎïaÜÓÍiè¥iÙ²¹¿/t¡ïßÕIÒó„«ãïíÒô»z§öâ±Hf`ÌÂN¶»±sJ®I¦Ê¥6 u,§4)°©¶ÀHf¦ØX^+¸Å–Y-Š¨àd!#)…w&ó0M)dhÀ Ê÷» Ch7#)•Hj8£PCÊ^,°kÅHŽ2ºÎC%Ï7èÝatNr™oë0þJ"RåìNÓˆq†™H)`%\‡ðküù8<½<Ë{Ç#òÑnÓâ|Àò0VÆ5‚“‰ Åiéè{ p Yûý#%*G²¿ˆÚ8T°MˆÀbq¥¹›ÖCúw¥‹öÙgâ³#À}™õ«ôX~]W§Ë-åöü~xËŠEÚ¥ š>]µ1Á¥'f´z¾u£º6´í„ÚH…?/¾wƒ¤}Úœ¤…‡aq´Áäãdí*$öÎ;|QÜõ‚îœv?¼„ÅX{§§nª!ù/ÙÚ~]ÙÊÖ’HçÅ0J×ÇDi—.pϼ «gLlCgø+¡¶KÐòÅÕ²gµR·¢¥kYKÑ‚¸!(M…i1 züÓ¶¯Þ#Ànª»Ð0¹P#Ë`³šAdºåXóꑱä\²EŽ™‹ =¿'›KìœýAÊhµ¥ŽlG¨‡æxX(8zœÒPâ†#~.½3iÒ<#/EŠÁ)U‚SmÂ8 Íåµ(ôÓÔžO.‹tI)au0*Û¡º!4ºOÓ ¤)€á‡g§aóV¦ƒ¥‚“ëž__ DÓ'8z:õ1=Ôr§ % $ž×jD¶›U äÏ£Ê-2§"lU ´_ÞbLê¸óûYOÁê=¬@…·^·ùìü4-<£‚ŒU¦”&;Ïo…^âëBP&œÈl°¡DMú"vŠ¯aTçì±j@Ö94E/}0·]«D8çnÈÝ 5²¸ U«—µÝ§á©€}]@W:ÐBœý,áçÁé §D;aA¤ˆù®­õé»f~¶¡ÔŽç[ûÜô‚ ™èëXP§x÷º|æÐtî#)Š@zJž$ü.{%™ï¨3¹+n^M­jP/ËKY…¡­Y ´sÎœ‰—]U`r€Vë"˜ðh¶œ‰pøQÙ—pœ4+¨M¹¤åÇNFó $)Œ_Âêl#NQÓ<õÅ% h_™úŒ1øÛ5ã›ZÂìouX×Ò}¨«ˆ =Îéƒ#%T®Fæ‹Iâ)|Ùò½q_–Cfƒ³DÖ9>Uoº![úÖ’4¼bBâP Ò¡´ÌGi¢%00G¨B°À8Š§Ž€v .¢MkäÞÛ£B<ãá|óâø °m<Ïh¤]¥æ7rÙtST"ÛP(aZxdîÍ‹L|µi±î#%spW|S:nˆ=£ˆ.¯:ZúÑ´ólZì&Û‚ t„‡-O‚ ¥qß0<â#$‡è9#)*ï‹@h<b”º¦ïîˆkô ^D±x!Œªìtæé~%óX¬ íçÒciNN^dÑ<1š/iÜÒŠŠpTP¸À3ßšcä}º1GÖI»¹ê‰ní ˆRC™m¾'‘±¹Ÿ hïîTyôæ—2«ÕÃ8³ 0ÂÜÀ¶=NËšô(춶Á£Ï µ\²:ä±Û]LéÖÈo©uŠázãÐÎW4ˆu¨†…ºÎQU÷#)ú¨˜£g@îª#)S‹ÐRÐÆðƒ6\×¼S{éJª\u#)LSÇ›ªrJh¯ŠÛ':ˆH«FÔ|­QXCJÔ¤#$ö½@Ë5CdÄîÕCòucë-7‰=+1¬üέ¹£6zòÐѶ*œÍÜ$ƒ—_^FGJ1œN³Oø’¸¨a6àDº¥Ìî>\Cμ³e«ÎCÔ‹fÈŽŽ‡Ô©iëªQbŠy0ó½ªQ¹ Gć«%ÓL¤°°ñ, ÊÚŒ(œ'8Éúö4q[Û5ÊE—!ASÍ«)Þˆ¼4'Îw‰QðfÊ9~¼x:Ø`ÐQ“³·ˆðÞ-¨‚@®4á#$¶B«n®<åàäpPœ…X˜á¦›¹1Ù}Õ`+CzÔ@¤€RÅt 5:}tÆuQ(zÜ÷Ø·¹ ³ßë8ÙÞ1¶é#%Ï?™‹Ê^PÖâi`Ùd]ÇÛ¤Â×êœuéƒ æŒÇ#$,ÃOq¥Ô-ÂËn7uNŽ¨fàƒÖ#)#%V(ª™1šy5¬žuæb†´AØQ/ãuI:ÉÕMwÓZn@lêx‘ʤí¥ÀR馵Eº´AÛm„ôg†I˜ "Ë8ÅÙÊ#) 8Xí#%OJÃtíïEçÝêó–rÄ÷'…(ZÖñÖ/õìƒÚ© òé–í@„Q»w nyéT[@i|¬ P#%  7yí…ðº«Eq  Ð„y,éžÙÛª¡U£Žxm†RtÞÃ(eŸ7ò¹ U¾¥ÐtÔC~žŽf\õ-u“êtCf&àÌ)«ù}ùôýÿÆ=~Qëú2‘ʃ‹|Î_œ:”j>Œ•Dÿ’Ë'ÛÓÈ|M)©TîÈ"#)V±îpúcé%•¥P*17B£\xÉÄÔ÷è:Y©Æ½0GëɾuŠé¾£°n#$“ø{©­;œç§0Øx0ð‡ÂŽ«Ç@Z#$ÉÙñ¬f¼ù¥E7«ñüד²¾Žl6þÛGáÜ<š„¯¿õ½bÒ•˜^µ›³_c=Ü)DüF(~&Ûm9®Ð4†G'ä/wCŸ~^ÿ˜U`ßCÿ!a@›Õ¶èŒï :Ê¥@"qÃv?ñ3×_wùÀ÷áýÜw}áû}ߪl$þ”?œ(2–#$°@ Ï®pj‹+ëÒ—ü’Š¤§ûgø‡, !Û¢¾hó0‘Ô]áA±(²· À©`ñ¿Ïh1¸ìÐ[?ÔÏå-㮯oeŽÍ ·;s,6ÿTßdÑlí¨~!‹ˆ ä8<º‘…öóí7#)š(&Hl:Û8œ¹‰^®fÔúüo6ñ`§†%#)¦½þä,ΉCébý Ô}ÎðB©Žà#$¼Ÿöò[§Xx5ÌG¬{AØ`ÀÞô{¨9žïoû_C“ó/Öæ€9šCÔ.È®Q_{ƒ¾Üu šYiØ^åÏßwûBù&¦@6ε ±ô` >£RÖBt›c’`dže=kÐ8 þìTûýK¡ÝþnÞìÞœò~„ÂÞ êŸñÕ¢U­úeöãÛtŒŽPKM=¡Î¯@2q#$˜H¯)å86ú ’HFÓ<•^¯d­ÒUÂ#zX@ª#$ ÓÎ(•¤skíþøý~~Êì#?m‹nÝd´ Õ$o—OŠçûï~¥nÃÚÅÌ ü$IŠUW\#)–.û–\>h“Sé4n)jJað+|Á·€¾Ä2†ú°9ÿ¤–­†ž!à†êü¡Ã«÷w=“†Æª-¥ˆ`4¸ÎŒ7qÁ&ÊFü@"äl57+#%aSíE8‰ãá±4æ£]ˆžØ €DþG¨¤4§ÏÊJ;HÉøÄ$/ÓåMW+6ç.I¿«ã¨€zƒ›ÔG@qÑÖÛ°bÐ}¤™ìMv#jüèn;îäk{J‘’ˆøÔ°—Œ PP‚‘.˘#%ïap]¥(ÀBÉÓé-ÀŠsw1¡z#%r$s©V6Gal‰‹9'C :ÊÓ#)àóÖÅü¨h4óŸUx^®RO¯é ÜŸãòðþï/Ê>‚¡,ÿ9PÀ<fÄá—º…D¢†…ŒÂ1¡ÿÈF¯þæóþÀ:觘ÊKïã–™ý_æãùSþÆÕ´‘û„³ÚžÌ%'èÁヰ;ÁcÿRã]÷«Y×#abå{ü·¥˜ö”dRHõpi2¦Žtµ¯ùëËË\u[ï»nѹ·ó£Œ†‹癲ÀÙá0"²HÛ>?rs<i)X<tõò¨¤ªõ·a­êŠQ´$ÓéZ©QvcÝÖ=à —ëÁÊm28{‹wínÃa\æ–'ä1Ô‚(5„N‹_Ÿ· ºÍÐ~©œØïü3>ÅýÝ̳îÔ5½÷òý‡Aˆë=eL¤£>Ü–Ô#)Xš·’Ò “à4žß'C’?ÃøT‡#$ü(R˜þ#ÆåA%S•.¼®zÍ­îu„:”Î&`t”àÿ¢g€hç‘fÌþw‘ÌXôƬ—kçÔÀeî#%J4k«Ïøu¢#%dt’ FpgŒbÝ’K!V.íÝ$’H™^«¼ùû6®(óÙ†8°àصß`ÿ)ë0Þôo5í#%^aÚàêFA„#%Ù=8üLa´è6™.pw;²Í—uK6ÁÄ¢Ì#‡š/à{öú½à]ê4 WûGö"øœ OÝýÛNÙò:žtÉ[L_Á¨4\¿q€cjÃð]þ ñ¶ƒhÉg©Y¿‰÷½^“çºãkÆv²#)„Î Ÿ›TÛ|ƒÞ\IctÒé‰bŧý^ÝûÃ=<g°ò°u:—¤2„´DÕ’¡ásëŒφ¼/º:iŸK´ˆb©?¢Qô-¾Œ¡ÂØKH1±¡ŒM™$Î.r¾x–°RÁ¾(4#$D"ð#%i`øðÛ¸?z$ òï¤Ár"æ\taðVƒ/§éÿgB>pú:{ˆ„J¤„¦Ez:”ÚnO¯CÔD4;#$ñô9·'zÝã#$¬”@D|.aŠ"©£Ç{X¢º4ϵ,ø#%ŒSŒé}öTZR¦·Å‡Ó8«È½²d¶K#%¹²¦šMaQI–$Ž:Ñ’˜1èddh Mž1§'·¸¯´54Œx°>â{f ³É¨…#5ÖçôðY;ƒ'CXpëâ, {^2nfóf!Ž¦OHZéN%ÔÁˆCS9"8P\r!õ¿áéüçÓÔ?ºå»oG×Ú…ÃùþþYÉAkZS?Fl¹®î?oÎ5àÛú~[ßz¯^™)"1‘E@zo‹ª«¬·ý,UÃŽCfù¯ß»Jm”ßb•µš5­oC"z Cˆ*ëœDKº;6ñ™2¸Zi#%8pvd’BI›«mb“3ZÍX\ɨV]YkÞõ­ÊÌ ŒÆMÃN°£#)=<[y¬J2þÜ3a7Ã{Þ:²Æ´ŒÚÛm¯òE Â9L²ãìøƒPYPI$9Àª!¢ ƒÊ€#$ÿ™ïêIƒû¶\9;ø7 çœGär¿âþŸezº-­ÿ3‘žZ"‡Øí#)¶©\ËT’}5{5tˆÄó˼ÉI~Šý붼Bó»ØŸR>ß­4ðàΔ h}„:;í lF,]„$$·|ÔºåvÕõU¼>ó\È?Í”Ät"š†ÒJwÔ["t*»ÕÙ¬$ˆ €r¶QI´|àÉ’tÿ»n5Œäй5ßwu¶ÛmtT¡Û1À‘·¤¦³·DÊùÌËÄ¢}dñrTÒ±ÏJMsá/MÌ¿Ÿº19l<ñIýWêÑ{LjçÓ.O–"lCÈ2PZÅ›Ž—ÚP¸vr 0yò‡8È*Á@A Åî_³]:µ)¹£Sšå¦ÈF©ß?V±Ãá#$ø×ÉöŸ`|ìu7}ÿ»+hôvùzúŽÀ0¿GéTžä~–1‰yrŒlV$õ–»Š÷¸? ïÚ·¸Së"_Úv|3çgŸÈª*T”¥"ˆ§ ›ðÏpSF[¨5º¶È7ÁVÍÝ›è@1ÆRðK:P"b~‡ÈߟK]Á¡wh?Ì‚ßÄ}Q8›þsˆñœ¢½ÄÄ´ f"y–ô–‡òÅ-î†ô.¤$ #p“iáàÉ``È3q°šnãåuoˆèCO €¸YùlႧ¢“+@#$„ ñ·ÝôÏuçò_î+¢[ç³nÑüGÑ¾Û !)†rŠJ =+ÞÊO|\B,À6ô½`)gÏB£Dûv‚y:«Æ/Ú‡žŸëÅ°ùŒ¾R{ÑÜs£§Ô4!‡G¿0ÙrEŒX¬Àæú\#%ú½ÆÓëƒáº‹Óˆ½_¥ùΣäÙTåcÕ?=NÍÉ÷›Ä8@w+ÕNÂ*Q8#À#%Ïb‚L&KFÁûCÂÁ€ ypéê®>óÄ8»š0Fǘó™òÚ´ £ÀŽùÝÄ©kOõöj{{{!` ¤"‘HÈCÚuƒ—ÞYéSúŽkuaܪÃMž:\èC¹9>v†÷‡ 2 RÇ̽<éI2yCoOqUôPú:¹—ûqõ–¾$ÿ-´1Å„ýçé+ô“å>K·­éÑÑDÑ”=áù1»ÝŽ‡_ÓùüO/WÒÜ@Ä.¦¨zTÉ@³òÂ2I$õ€\U<‡Ð·÷fV+›)@H‰çñüâþ “è:‘N¨àl hiÙÐ{(¿ºÇ/¦QÕçh€œÚ¿nfµkr­¬D÷b07½ƒ2eÎ2é/†F>#$0…xý>èì10GúUIX t|C<wÏW~a‘gv}J¯äcì<+¿„¢]âæèà¼àS›€±8†¥ƒ™jºoÀíûŽí‚Ç<Äã!’ÎÄ`"O[tž‡³V¢"R>RQÀɹ ³öÁ.&Õ#ƒ:ë“„K‰825–6ÆwåòqÂ÷rý£ø;_W»ò„™´¶ R–6e8nÝ3Í0”`Ì-u‹r6ÍèE±½„îzLy†çКtpp"˜UÜ?¤ïè™aƒ"H,ŠÈaBH„–½<øs;#)¢JD¢‡éätaéa*´ µ‰bªH’´ÕÛÉ_(>î?YèDð < ÃæÉïµp;7ðœÃR#)ø ö›NÍŒ;ÿ¶o É^jªQ{žÏ™âþÇ×_ŸgKS ·…àÕÜotcz’zä·­?– ñÂÔªhrOòÊÿf«(Wÿ=Å(ÃkyiLÕF9(©t½àÁ ™¦.š‹kEª¥(šG#$×ÃæêëîžsºNÙæµ°>óßGb˜)ɧç¤ÒØ=§ ¹Y*Ö÷vÕU³Xã#)¹Gö&^·rï4|†Ïd¡SØ#ZBu—ßøŽ2p)æ+T(¢ Pˆµ>"w§é«\‚(m¸gZ¢ »ÁŠ#%Å|‰À.ªÏTì€Ðä¸K‚ýO51ô/™Þ+à6R4xènã7ôò€Xö—,,X!ê(->Â/ò1Ÿ\­Z-¡@ò3”{¼yòþ"¡ü_Í7îŸ:=Œñ³ ,Ϲ‘¿ªoBaMK•àfšÞa—Š1+&¡"$®‡j p@CPî“öÄ„Ilƒ#$Ê«s2”¥õfÞèbǵîJë=ëhµ~”® )G”Õ(DrT#̇㊢9¦ CF¥¯ÐvàyìËÚ‡™þQüþ9dRdƒÀ‘’H¬6ªÒÆÓ}¨lÕ<áøD§nóE25#)>TjY((9‘'X´ƒ!ÏûŸÑÀ:<û¤RÝ/µyXµê:!…¡çöÒ%ò´éª«) à}w‰Tj–EdEE‘dJfQ¦úû;¹­zÞ:­ÈÍ*Ce²·uç™u©åÕKD´‚ÈÈÄoNAåôz>œ¯ð‚oQ%лëòž`õ¾Þµ#)#)#%ëãpÏÚÌ EùÉ#$–½'¸€X2W“ÀjlâÚaÖm~Í«!ú¡CSj"˜B Š#$PL,àÜáRŽ#)CžÓ4,D çG,-d'ƒx‡š¨ºîäìÞp“ÍŽþx×^UÐ/Á~ Z[!Ú4“fäêÜŸž}TRN5 7vI‡:ŽšUÚpÊ:ˆóûwnìˆVñèh9óGc–sÇÓ;mÓoªÛ †Ü‘.0#%¦!Ài» g1‡šÚ{.#Ò'—š\%tÀ•Lú#)à–€DøþW•\.[ºjŠEQE¿…`©þ0?‡Ö|Ò¸ª©¾ÎX÷q}šì!ZÍ|Á½ów6±ç©PÿÌôclþ´MŸ`´*ÒÓ0ÃL˜a© ‘Ð9†õ(õ,’¬’HúÜûX?N#%áëè¶ð4 4µ€jbA Ì©hxÆÙf"ŸI¿‰ðãE!ò`Ciš:1HèQ±ÅÅ åÓüœdDèÙ±H*àš`0Š|ÀPø;=>ÏÄ~þGQy0œ³~Ôy‡z¡ Ã¯§oLJýÔQÜBØ0CóÌN¾_.å}|ÿ®eÆ”ñl+¤~îVÈæñŠÈÆ()(îOS `Á•Àd‘a¯®èÙ¡¸ÃgsÈBø—Â}gÝÿ]} Ú±þD³Ñá•#)ÞÌêû~¼vsýâTþ¯©òÔΰôÊk‘#%üêi’„e ù”ª´U£²¶ˆ‰¨@ÒBæÆgù5=#>úÐìPOg–K³V0\###T}Œ(æ[îP´Æ¸“‡¨H“‰.b©B €¸K‡LQÕò#);»_Ìíȉ@çЫH¥,A ‹€Áƒò¸.ØïÌÝ.<Îjäž cù~°±ÙpÖ}0¸rÝÚlÞò#$¦õ;ípp"#%€:ýU’èÁÁA2ÜŽ—QÇ9†/§nƒÒ§0æ!½íÆN;¤²‹pýAÀSP jèð+½77XvÜ1S§#$0óÄ:JåEXw™Œ`ÀQ΂½}h£W6xtú¸B“ß±T(¡œOEØ Ã#$#)ÔXÈOçÚâï×üþÜjd­èÞöÄú—m™ífÚÊ韵òÿ¥šâ“GU/±ˆü˜Iâ:J@€Ø¯Çéµé$U$M()‡jC&Ó×JÀ£‰Î{Q#)Ø›—óÎÇõ³œN«0_žÚ¨Î`U‚*dýi(®Sòª--lm,L»XWW#%òû;„sª*ã°wö§ÀËS7Þ3JxqÑÿ³°ÚbõûE]§*æ¿&ιB§ W‹Ts<Àû{Df“Z]OBd+î(; ½xóâìDé‚È° ²8%vÛx„ôÓý>¬jI&v!Þ¥jpî#%æåÏók{p´É6^@ä¨gCóSQ3;W‘½¸Ò0!2#%^6‚)a€ÂJG‘¾ôWß èCÁä¡×ÙĜгÐüBæLŠB«Bª†ÖKš9éãÔëÔ{§ŸÈÝEÚS(G™ùBlÀ&¸ƒÁQõº!FéÔž9Û»‚¦PÞvŒÈ|h¹Bù‚ÿa*2<~Äkd‹¹{`€¾¼LMÎZ„›b€~Dû~‰Ùçówö–œñ?¹¡nš¾Ó£o¯ûÄ‚"ʪ#%•BÐR8¼‹ª <{#h`´OËísã؇_,øúÙîWà×é#)Ÿ…J‡¿é­˜ÓFÖ Îh؆ûóe}ߢÀû©9F#)fƒ™ÌîðfÏ?ó~›4ìë fñ`9Ènúªgêí¶ÿmYkíÙ·ivýĈð§>⦨¤ˆ€MÐ!ivB ¨€€H7@h#%‡3#%z$GäÊœ}ˆà…˜oZ ð$i–££ ˜ÂÀXôPŠR|ñ÷w{ƒ·›0ÃëCh£®##—‚,`²#ûúDàêê‹ÚG’óyv¦=Å@Yz„ÄKAP ÓÌtœ½äÐT6üß“cqôæ6O@èSÈq¯,pž2‰Æ-Žƒ \«8 E1X?éÌÄŸx=FÂÞVb‚ȶ.{D‰¹¢v‘§°9.ÚHk) Ø0–š…~>ÿŒú#­ÿ=çÛÙ€V˜@b·Î]×o:⇊ñwkhߟ#)i™õÝè1’d%ˆ>§ÄØCxÞÛçK4ñn0×#ý¦te8îg#†µ‹Ár”Cű¡xÉèñ»èÜcë`•íÍ<£oW×VHŒØI,×Vΰ `«Œ#)5ó~-sñõË–wiz§ž^DX’Ê sl«ÙãèD<¥uÚÃ齧?oê´"{Ã5ô ÅÈ ª¿:X"˜{“ºzϡωÄ@ÃãÍWß×í¸¼ˆbŽßŠSpý¾z;½|JE”B ¥wý˜êN#$y3ƒòðI‰"—·ò߉ý§]βs*íñDG­[j"‘‰cÇk®3~ݼÝÞZ¿ßVòšÜ_êOì౎@á‰r`*³¼ŽÕǶ¦ç & Ö#%?~³óœD÷+mQ±Ezõy„ëºWÇv™K}o`"‡âI¹˜|‹óÿˆŸÛŽr¼ùøT%4¢ÔóþŒ(ù–ãó/Ý7OtöÑ*£åG<š¶×i&ç)È&䳑ô9{žÃ§ÏÆò~ÉBy XK}2<<?œ10÷cþˆÔn†˜öö@ž™Fú¹ðj`sûz5\Ÿm4b~ˆHÃì{Anw˜ôo/v½ó¢¯ûu:=E5€Hg‘PÊ:qü<mìfÏåÛàn„š*›oaØúáì#%:è/“NûŒÄ=°,ifüËáHSA@ÒThž…•'/aP±@U„XAØYÚ°ë>aAÙü-³ú¡Ôll#$Ò%ˆ»½ý\KäHâG¯ð? }Ùv}æ—k„3®Ìí`ÓšÖF]€”‹h„ !7=g¬w=Èn B XQá¶û¢»’²v?}îK¯^”Æñåò€ä9æþ}*?¯O9rõæ®[âÿ+•©ãørCNÀÿyCÆ3ú¡#)àÑÜÐALDz™¼ç]3-“ÊgyånØFÞVX2h¬'à2dA`¤À!Q2¶Âjjú‰˜æ‡q]*j…‚t ðÔÂÒTÌRȲ§šØ&ÔþóäÍ Ô„(ÿçŸÐ ’¦¶c¯aj1ÿH~Í¡f(-4ùê"bâMr“ANìW{¢Úi«Vn#%èÙŠ¾á‡hF {j#%÷D™q+Ô´çhUˆàÞ Ä€ße(eÇÌĺ^TÀ¦~®“Oñ½ßù|µ®lY( {2¾"£Rñ¯àŸ† )þ“¤ðU+%E÷¸EüòñPÄÈæI:ÿ´i3 ÈýŸ£•tWþ|ÃÍë¡ù};3˳ž¹Ó§^X0¸þEA¡Ih²ZKH³N-N½l‡ûû”ušùßïþ¥ŽpX>ïOÿ™¿ØyömD~ÛÌâ"¾TY.îËšï1òÇ{ë×Ú:yT{,eÃMÙ"bŠˆD!N_ÍÈ"oñó!N¹ùsÚü„y£Q‘AX¤,Ć9ý.)B‘Ôè삾×%£¥“Å@¥Rð#$ œ0¼*5#$@åì'ŸfÁH“5BmA$´‚Ÿ¯ƒœ`‘UãÖðž$Zä=Î#)$»ù@‚ÃÌ¡”-Õ»R¨$#)hî‚&ý¬ºsR?³œÁàg#$Jª¤6 @ÊÄxèF#)GŽú¡W&IçýŸÈìÁñëäw$!¡Í·þc¨¤\²L¹pzï´´Ÿ$àlCÀ¾ñÁÃG³ïƒ©·´êâ=ÆÑÃSQ—ä_™1›¬äohü,£ öÏñã<5:ý°Jüµ&/£’ÂKDÖÖ?bì&Æ8g:µ‚¼<(NrÃ{)t¾Á5úÉï..S­b<‡Q´¼>\‚€áwAÈ¥r õ²`vN¶?ÝÍþ”ÿ5ñ5Üç·l<ç’[Ídm¢†k$éÔþ{ ¢á•bC¬ ê…­nÕ-eòt×½àŠ(›Þˆ‚p ÐFÙ¯Çúõú1_lþåéz…è/ø^§ý?ïO`«ËBòþ7f d#$àç²'Ä+T@~_Ë¿20A/ÓZ¨PínÀ‚ª9Cm?}ñó¾o·ÁåÀ9Ñáøªô>ëµQØH>ÀÀ"Ü]4¸Î¯H±µm䜅ð{³´ QShr¨Ueæ#%v1m£Q£ g à>»„:F+¨ÌÖ'ÉÆöÆÛ'Ðïaņ~Vtš.(Fý±#%÷\£ü±Æïcî3¨j|jÏ%Ï£å#$pÜ((¦„A¥äøÆãÚ\îtùϧ£ÚêÔz›¸éx‰àŽ1¨¾j—ÁâZî£Þßß!7Fí¶° NÕ;öuŸ‚ø{7‹ —CT.­”9XðÐu¾úèÐ-Wûä‡9@’ßIÌ®¨Ê/„t_ÙÿµÚ(›åd35²––ñMêopvGW=töžŸŽ~ÓÁ9GõÕù>³´Ì?ô^áÇ«·²Ê}í wóvñËÞMÞsXVÂ5&ýöü2Äk·Þõ28éœLðÑÑ{ïYùu95·¬lÞS¶ ­L#%%ÅE…ÁnhDSÞÑÖpB“¢¹;@:Óf#$x)|8Ú=±q}žDk˜ë8~ò£½ô_t›'Åëi¤X´Ü©l Ïa‘9A¹vT¨H¾»éä^ò&¯Æ±ã®ûÝ&ðà9;¹ˆmÓBåÜÙÛe‰êƒÄë± ?8 jÛ2òÛYµ©oŠ½“ýWäî×Þ;¦š’¥š*fˆ*ÀA#œhÌñN:rÖh½k–q‰F‰g§KØúòýÓÇb ë‹ìyþˆ‡ò~¿ËØ]¤</^Rµ|ëÓþ#%LF>'û=Þ±ï?W×/Ž½¨è21Š´ôä¯a#%nÏ¥T Ì3«„º]¥„ZÏÕ€g馸хF5Ef)‹Ñ#)*p¦ù·\ ÇÜŽ9â1³k;\¾ú¢tŠÃº'óg#)J·ÿ^úË箇ǩôæóéwqµlê4-u«Ò…qõÜõ§oöïÝÉç<øŒçËÖÞ3RãyïÛÀÅ×rcÁ‚£~ÈÑO{ùñÓatÇúÕÊ7ôÌ i ¤?MÒ^·N©füM=;k¦š†ì§ZO‰QJ##)1£ƒ]Ç@ŸüïP>Tú?jrôB¾ª‘ßÝäfˆ‹½\l´Õ瑱æ"ã÷jð›iã!±y<„C™QýɼÒôßYŒø|”wÆGýÍÇ3@iîÆ39dx4—çðO™˜1£¦’†Akéù?jƒÏçÒc~¬yAi!˜tÇ€å ©D3Š›'““õïßnËØÃz"ö71·\MÍKt²=þxGÌ}ÙË8õ5øâó0˜ÒД 7ré½³2cœ§.%麗 @ç#$ÊvÁþDІ5×´>4ÇQèòÿû“NM´@yÔJ w=Ú`:  Ž%xÛ&Ù¿•¯X”WËôÇ;òktÏ:yĶ5d#%ÄMRôÜó³–ZçšT,ª&€«I$ý9¯¥k ÔIP›w"‰ÄpšH›•H™Ûs#%•ÍÉ-¼¤9ÊÃâä˜z#%z"+e¨Á.·•éK{fý Pgõ±(›,þ°ýïòëb¦¡êX¢Ì;O)##òþ?/ôG½ñŸgèðé¿ó×lø»Bš…y¿ÙíKpŸè›[<Bgyü±¦’*¡Ïû¿Áïñ:ï±°öˆÒØaôJkõýoÆi³"ŠdÊžªOͳöw\þŸÜ>Gõÿ7øŸ0ÜCûËΖƒ†Ì#)òªª°ÿQç`]L ÷ÿhÂÆÅÛ@oÕØ›bq+vòuâs4#%¼Ïñ·pô»Ù? (»s{Ä¢4ÒR$`FÚòOòh\nÛK†Ñè:w=o$¢OT ôlu:Ü¡„Á°Áf³Õb6I.CÑy‡³1G¶I`ÈtÑÜók$à]K¦;kyTUvI·¨º[ŒíÎppå{žƒžñäçÑ~· è½piF2H`ôDAFF ây…Ðà¶gö›çë6ôˆª±ƒÒ©ZªUqÔ†Àu(2íäg&@N0Ï{ôòdi†7l% Äõ~´ýUuþÄ)ÀÁ,Ø”a#$Ã-î;gáåb õ”stëE½ò‡M æZNþePž£òîú†¸aζÖÒ_Ž¸]}ƒ>x¥P‰ªœY#¬éuÉÀa™ˆ0"PPS€á)¤– ÄÐŒF"ª4aF Ì4Xpl¾à2ŽŠg†ì ãí Ü Ì+¬Bö¢Ò›Kì»×À¹±{w»ˆ Ëh3ËŒ†OFU R÷Ô²’T p³ ÌÌG2mwU@ëP¨O—_{÷iËî$)Ñ—OªŠ—»âæµÒÖŸœ#6lñÆït~wuy©c•]zÁÎve@À‘àDÐÉHMЪ¨Z4d©‘bŒ#)³±RjE`Ò2!´Á ó¢†\f§¬#)½‡Ò@)ª#%HBÍU$+ù_ãÿ3âß•™ ´Q~þùºk+",#)ÕQAøQ,¤‡ô;ê$øHv‡¹TA*îÛ³éwÁ‘½ý«ùØöE{s¯sX‹˜ãûz— ø@sôéÐÕV+åÀÖ?ཥŸ ì˜T’àvIà§"ý£:Z6v9jHÆÚDÁ‡+!#ö¯v+mXí⧙uþeßâÊc^Á™)6™®Õ|i%A±ºIÈÄ8#%ªÉðöÖq‡wØZ^¬Ú1Aµ_q=ñ’“Ð%0a„Cñ)ö˜¦I Ö1ˆ1íñãÝmÞÁáí܆Ûør««é ÌÆq)€Øã›Û»mØ.~ãm‚­¢wqaEyJÚ.¬ð3d*Äì,(Ô_Äz¯Bíu4ì⣤ŸÆ¿ÖÈ“$¹‚G~˜C!L,²o!r爐u‹@3sÅ.sw&9)(3®ï#)$]G.ÎYàå4UîHc)Œ$®I©“Íë(è47„¹¹S>¬§mZÕ_+_ƳÏzëw—Èû£Ë$qã9iNŽŸ=tõ×g<Žm¼µf¦I6ÏKÞ¸|ÝAÊÜV[:ÞfqpeãAÈ.´ƒé;ÀädâÚñÒu;öèNí(âˆl{÷màó#%'.R:.ýO˜pi°ÖA¹þŒŽTÏ‚ør†É‘Z†† ž;˯[‘±…„ÎF'Aw!CßüµM;}^Í ºv‡Ì¢uƒÒb–2~8#$#PîTߧ‡^Ý|ý{òá̪(ÏapA* ¢=è––ßÏ^š6¸µ.ú0ÃÎbmè¥U6ä®^.i'¢é.=36ýàÙ õÙH0˜u¸Î&sZv,A"–»œ ° ” ŠYzøÇP‰#%œàEY#$ÅòÚlé2i}"p;•Ã‘Íö:œa¶œ[%›Cß„jŒ+°Ü—7ÙG"&É8#$ j¿aäóó‰;ÐÔØó ”Dó8ÍØL«¿[mâñŒb¤;r°QˆI4Òd°”É"Ï6ó#%±çNž!ÎM'¦3z÷?›0Ì{½½Î¹#¿Ü°y±Cšöa“â p(Tv:n.¤xðÆ {ÎäQdé… J#)•Ì5àÌ­6 »&€qÓõÕEv(ÜS¥ëpSi6›±ãžöêÆíÉ íà¥ÆðŠnIjìxnJlŽ§!R„;SØUÒPÌÍÎ åP=_a±‰ŽÂÄj#% g~¤­®q$wtoTÔ”ØÑåŽ}·ÆôcÛ*ö7B”B$$Z€›¢¸å‡‹Í`ã1É…UmÊe¥ÖéÈÁA‘r‡¿xÄ„ëL#$Ø©‰:#$B@`ÁVXy£¬G"PRA`k4ƶK³.ç„ä±±¦ª5"·QÆòƒccÝõÞo¼û-ʹ܉•Œ™ ÀÅQ¤â­T IÌ£ÌÏNØrÑ,\à@&Ø+ǯ53¢!ÚÌÄ©™—›™™™clÌŠ¬Ì™’J±Û3Ñ/#¢þ­´Ïvt‚8°ÍßV\务 ŒPRAPÀaì‘´ãyÙÁ­-?‡¦ý“VW›<1¸æ½DÆO(£-Ü[žß„-±Ê~yÕQLÓ®²Ñ0DqR±Š.yRÀ¢=.½4õ\ô––[®‹¢s[¡¡iXïÑ<fÁ¬M“32Éð±D‚À˜B™Â8[# ;Ž+=ZÍÇ<0ƒŽ†F™:õBP sÜ®Ýù4¨§ÈBî1bÈ”µI.©lªª¨(ƒgM&þÃßÆôC:íT%{V¤jQaF ÅÄ,Y™ÒKb_NiÖ˧Çn$|n–›|σ=j©°*–4˧p†S54Û‡q˜ŽSE((ƒlcü¶»#) ÿÒ#)!ÎtœuEEZ8t+ÌN[›*„#áÁòÝs.Ïóß´Ô#%@#%#)“½Ø8…Oq~^‘È´Ói!Ç¥¤GÆf±xF )1õtû—¤¶| ׫véǼ'ö¼¶Äç¯Ñ@NäÇ@®„Ø”^¹nj;ù \«•ù·ÂkBŠeƒšÚÂ1Ñ*lˆBd>õª5fɺc}´âo#%o! ¦ä”Ûˆ”ÙàæÖ‰¬@ÚMƒGnÖ9¶<²åÈe‹›‚dս‰î6) ¹ÁF”Q™ÆIÖ Ñ¥£ÅNKÍ£SÁr„”ç¶ì0m#%^ïfPÂg?äý;íì8ˆTB2Ql;DÉ‚!’ä•…öÒt:*“"è‡<3˜)#$™!鹓‰,¶B 2Ì„‚ c M>¶ çi(<¹•#çM˜C—-M5*¤Òp!×CŒ‹©Ö¦u#%lf‰ÛÞJcX_è~¦GÉ4Ï…ÕÍÂ]7ÙfÖLƒBÒ=À1ݱ‡ð ë™6Ÿ(šw‡–#$¤T&#%õJ8^mÜL÷3tLSPbã3Õ©Ø5Æ"LÜÏ D#űNÊ”$Wêm£H)&»`5Æ­!œgdÜ5Øß,+³ˆŸC•i7¦(äÁš3M¦4妵«Ÿr-óãØꌨÁ¡³ ÒòúOsÛ#8y…pAŽ»Jwx¿UÐÐàvì#$ÆHñD‹Te%’ÆÑ4Ùªf$ß3‘‘.ñ)1Ð8É ›œŒ<A‰í:+‘M}™-oÎF¼³@ÞG¨¤·´·=(ƒ%h4Íä o ÙÆkK€äåÑ#%å#)¸*.iÒg°©(,—}9¡Õß,¤|$›coF@/_\l".íÉ_ŸÅ&Cx5tkB.»Ê\÷ûvhWWÖ:5Ç/*ÐÚ’ñ+ùø=õ’q>^2åÌÌF†˜C¿ ÀÆ=C¹¯uÐH 7U’MŒÒÛ¶âT#ÎX ($ý=(Ý‘ö˜1önS^@Èw'Š)õàW«Ð£)«0i)ÅW¹6³m´ÌâÂiŸô4dXjœ Þÿ<0òN~¸£Ÿƒé»¶Ét¨Ž2ò.„èGØEuÿYHBû<cóÑ>–63­ßüèÿqŒdGu‡f3Öø®+Ôõ>Þvn/’ÿÄúcx[­×Ìj¼®¦äÕ«#%ýÙìAé‚…¤¡íól #%/=7wz5…Ëƈ¶#%ár#)XŒ,Ù«`¾´$ÞüÆÃää â#õ@ð QR ¨4Ç”ðàžþâùbŸŸÕ‹1™þ¸}ÿò3ýóòÚ¬Íÿ6{¦¦¹Vï7ÈæESÒT„$mûÀÚª~Å=›]¦h³6H2’#)Ê:úüÝÃÚ<éI#%Ÿ‘Aÿe›‘}G0sæcæ @!#¤LK^wQ&©¦l¥ ˆ¼¹ÐâÂÛ€Ò0€“¡^´SDŸ›ô÷Åï6Ú-ŠÏº—#iQU/×e·Õeööqå !Õ#$¹ØÉ K<âá•è(ï°‡ê>Õ?¢H—`*PÔ#$QÝ”¨“ÈwµÒmŽVYÜOIê²°ÿy$ÓkUDàt‡‚x!LéªLC̯ÙÊH„"F1lK E´WLP÷æyˆH!$'Ý>éršåŒT°"f®ªE@7 ÖÌ@;‘CÓHm¨@„"âk$ Ò¾ý@À*&S,úæÔ£Y!"ZИ¼ó¼ÚdFQ¼µô*¡X‰ ìÅ’i+æj‘ CH3 µ²(Ý#%zûNñ•‰ûì}a‰¬U56o“«‘´’'#ªÖÑ~vÅ]~%Å´R$@èA€ Èé eSí§Ó 2cSÜB¡‚ˆun%"š#$hRÆ9iet2w…[NUÜUÎÜzE rãÇ ì?ˆEóQ2 C¾#$°FçºçE¼DU$@$ Å ‘GÒb;Ho]·äUUýªÚ¬mXªª5¬ÒAh‹‰F#8q~€›’Iè·çQSXDáý¯4Cí‘#%1×m(þÀVVÚåâ’Øj°ÖÒ0h2#$Â#$Æ–G¨b%Jj7øÓ^ȼW¥”’E½îÞ=kr½›{.\Ì5ÝÚI/.î\®kðï<ÛÅÍÒ9ÑÝ\ÜÒVWuŽI>¹ÖKÎC¯kÅÒÐÔ¥Oøsà@˜5" p¯÷NB1`«$BH–,!ïz—°ÀÀH¨{&(\3þYC`#)7t¬:ˆB@’3ˆxN·hø^OO¥þ,>úÛ*ŠŠçŽ¥!Õ#)%F±7ð:îj£Ç=­1c-¤‡Äß7 àü ®™[8V:q.ÂWã€ãššEã$¯Ú,#)ˆb$°|^"„l°Ùþÿ‘kàŸ.MßÝOÑS #$H©œD‹'l%b( ÁŒP"#)GoÃCD™ìâ=>ÛòÝ€ è•ý¥S#¹«5•ç•|å½MLž¾ÿrI¨âðd²q`)N)*çP$ÙÉ®°.Ô¤™"¡$r(im ÷R¦÷3šXR\l“Ëò±UÀ‰ z l©¿‹u.ðþ¬=T~ã –ý!Cµ³û,‡ÕýóžÝ)è§è8Bk\†S\ý_3ku'°û<b~!a·q© ¶­V×}í‚]{‚ýdëßO.× !ó.®jübŠ=îã³.=ß!mô|OpÞÓ‘W'­·~ÙßÃlx'—Så>[o“¬(qôŒþ9˜‘ÛlÌýºBŠ›bsYQ}'Ý~^ŸšˆBUJ©E'_o=ÉÌïYGB ªyé3¥ñn(ó‰  ÐÌQöIøƒ 3!d/E¾qN!;#%ZaØÊ.©VOBOuåÔQŒ…†Ÿª'Ï%Dm‰°eüéÝV³K°ªâ”0‚¦ÿ#%øߟÃÚvý‘²Ê©Q„U2»àMÁáÖ”#%E>ø\.¶1‘`#¶#%m4µŒ’¥[‹M¦ªÌi¶MJتMVThS2­K[Mµ±ç8$œ'Æq·Í*NPá±ÄMñRE€##"$ˆ/G´dFȾ|) €Eœ=àÉ´è[øjâÇk†@(3mQú"pÈU€ðÐËG8“21¡`Êôƒx‚Ø#)²Ê…û¢¨`¬X²1Lãxi ù=¡}²èI)lHÍ䆲‡µ,†ÑÍÞña¹¡¤£"Ð4†´¬À_%a™1&@`˜ªê±Tv*ºh @¯DÐÝtÊ lšUQ mi"ÛE­8±Ä Q5/åŒFO‡rvÀ¾œ ‚UR›tÙ¶˜Ê€My#$X›=úq‚X¤ m¾þ\ÕJfÛ\Û›%r×e3bÉR`¨¨¢´#P© "²/³Þ÷ʾ2DJ€ãXAd‘ÁQÐÚp|zFÖÙä}>¼-šhÛ-!‰ƒ…Â&Ïr+ÑÑ¡a<ž‰"kÄî­NÛ±I?,¤‹ÞèA 9ÙO×Ýþï³çýÿ‡ŸüÿêÙ² ‡žžr½OˆˆÉ ;¼Ëðe⌠L¦*¤ 09FˆŠweB0cñ4B!E0*!…_5S¡ç6á{î;|ùXÅ\ÜšD´#$Ù?ÑdògÑ•SýPùؾÐÙþûTc ~Hx=˜ „¶Þ.jî]Xcî©¿ê¤èçÚWo8É?w03‚ˆ+PT2ãë6|¨% CÕËýùªùŽôêmÜBœÃÛt%(j§eódùµ#%Ou‚ *}–À©9,˜¼ç†yñ tœ&»Èƒ!#%F‰vÆ ·›É9žþ“£ì£¬ˆìÜP"9Î#)À"üÜnãÀj#)×búí‘þ«5&fÌ#nç7§Ò{7¿t'xC±¡Ý›s{é¡é¶{ñ,Ÿ~gSÀ¬4ù8|^+s×÷åÈUQx< ƒò–¤aôwȇ´ùÓ§„ÉXæÞ]û¸¥Ç?'ï§Ǥ†Ç–™¾Ýþd¹Wçþ98ù#$ö IÇËÄ#%`x ¤änÓž!Ä2!f ï:¡`Te0ÚS?$¡q}ç6¹½aÏ·&·±9¶û<i#%¡æV˜:gqNJ¤ôAÃF+úfîÌgTÀÉ#)r2"08O$ ñ‘0±Å¾&6€g·u‹7êCÄ€È+#$ˆ°¤¨¨zGÄ<hï#$N×Ú‚ç’êSÍ#$#%™^0X€ˆÝ¿gÙ°h ‚¹>Ñ÷l|1$QJªŠ…]—è•uGÝÐñ¿˜¤F æTJ¡`,‚‹;ýÿ¾PÌå jTV 'ŽH«¨<:üº8ò˜SŽTØí•°¿ZCÏA"éŒ`}“WÙ `ƒ‡œˆöï¦Úâ@Ø#%ÅÎlÜÌÃ>ph¡E‹Íd( >@áôY2tz<ý•=µú!Ðu=þ•<w‹)½Vcðçø/½Éâèˆr(Ý—ÂÇZº«%*¦@èWÌTyïÂÚ“¥fÐArÚ¯í†í¤Þµ¹ÌC™DÏŸ3Ñxßù¼”aõî/o„Føçcmañ¿‡ôf6+¥þ#%5Òáߢ`âõ¬G­¸éÚ».ž8ëµÏ+‡ßy/¶y1в÷}šFÝm’¡6%Ê;pûgƹë]øÞzmiQ×éí1geÙFMØÊÏKê.½ùãÛRØnyÇ2@ôAțĶ/¦ç[:òm¨4jáÈ/1ñØÕÜlØÀ·é‹cUnù÷ÁI€X+«vPé±f<{– 4)Ø‹HÊ€¤õ#).ƒx¦÷36Pž|Ò›¥Ôx6/•ˆ`Qèhø|19°ëÓyõÜb‚ƒŽ§œ³hÓ[ûŸs{¼Il0ršÖþN8»uÐ2æ:H!Ü£sk!Sâ/´LÙÍóˆ™J ©óX,ø¬ËSRªƒ¸ÐJ»2ë~°‰z'8uÿ-xÛ¨U0ňô?9ºõXlK£<-Æ;ˆHBºï‘³† ¶ÏÕd?¢tDò¾C‘­ß¸†Ã1]Ä|<$kfœ¶lÑrú8íèí¤ê/@_”"ÍÞ\C6Ãr<ö¡ªâtj|=‘~&@8ɱ»uèÝl¥í}͹± AÈRfBèš„ÝÒÉØÖeÙÆÈpÙ½<v7Â9ï©i>½ÆË7åˆ7s¯e[÷9èy*²ƒ&ç%:ò"ÕT ±ˆÄaììñ9mˆ&1,Í¿;¿Dc«T£’Ê¥SÙXžïo×SÐõ"€´zøodÉ‘º˜`•èêdšž9ö½k„71лȘã$–Û|Æ„Ì0²Ž;v¢r]<½õô/[hßøËe0‡±ò÷oÙÚU>œNʾgžúü#)×8ÏÙd ÓÙ¢¤d¡Ê‡)ZRH}Mê©©ôxò‰ÞmÔ±hm:qáÓNJ½JéËàÒï`8®ÅËŽ¹²«ÂÈ#%‡@ý¾›T 2)§Ë†>ž­ËÏ*‰ñ¨uzñk]ʉÈÞ꘽Œè”Á/#j )HS°¥6KWM»'uu,Ñ4[SJ×oÍç-ÜÝw]kÅÊá ®XŠÆÔ,U§A#% ‚QDEÔáÖÝĈตš—ݘát¸¨¥ÂE`‰dsn°ÃjH$‘,„a$"$`Hˆ’&×úgb;Ãóò.ER#™à±dÍ(7M‚´æ·‚ª$Úú¶Ð»A· _—®”‹0#%¤¿BºmDb¾(ª®ËT°ë°RÙ”¡Œ¡î“€!î›çä0;¨ù]ö·L‚éà‡ß$R@AÛœ›c!Fjd °¤“vÖ Ó%šy¤ÑíJ·vÃɸF4ð‚òaZîBqÑjÛ Ão¢Št|kK•#³m÷­{Pivê&fA²¿Vp4À§TKL^À*¯¡çÇ£>‚w0N(?Ÿ't‡é^útU„»¥`%Ê#)  ¢!È”D¤)P9Œ²#aŠ²R[ H%…!cænÒW:¤ÌS¼‡aõ <&_#$5×µø¯Ç2±¢ÜÕcUÊÑ«õ%¼E 5H‡kLB‘1üh¿ŽâzëÀo$„Ç!×:ùë KÚ›>K?[uUìîzpª—äÉ{p >áÞu‹#$«—/¨·‰øâ‡'…¹2ÒÓ-5‚ç–ýÇÄAñChi¡%óqè)çÂòÑJý¾â–7"<ÃÒÈþ $—b±áäA²@'ù^¤ÔÓ3e#>ðÍj‘ăs&æ…[ä‰L~çýû ÃÖa–L¹ôûü»N}/kñ‚2Üa±Tqº3¦À*ë#$Y…TÔŽP¡=öË‘Õ ‰%bÄPH¡Õ(DY9žS¤uW­_+hwtÜ£PÎú½ùH@bzRÖš”’l†ú…SXžbZ‹ü#% !·[*®æ‚eU¸ïÐœì÷' ÏJÛ‰Àá[ºj‚zÔ¥ÏhÎËOi)œ>̾TLNÀ Ýå nJÒ5'º8 _;ÊšËmËÂm£Ó»Lœ™ÓñÇl2Ãu(8“Ÿƒƒ)$’¨>›xLÄÖõx2#$Æ»;"Öá „Ú]ÃeŸB ÐØ]B FÐ/×Ä@l#¤ØT&Áì#%@p Í(Îós¹,Ô#%FAè@P€×D$%Î^’à ƒÁ K dN/ó¹½ÙJý™ñ¡Ë3Á#%Â%ÂL0Êåy“+H½B¥E@¨ŠÔ]zJÉMAàÁm#)ÜBÖ#8[]-yK­kß|«Çe{2—&(@¸³ÔÒÖˆá="^^8 c$)i/°xÚ\5€ Î)VK+u#I¨†Sa«}ë«­µäšŒ,êR“¦Ë¥AåúùÎx¾Ùá+ßPü0?’$oÑ6Ùƒ&âpl5„ˆèÇ£¾±.¶ŸX‘×#)‰²ôµ3~I½™R0¢„x w'#%\øqO£öžA 2j”ŠJg#%OWe7ÆÄò9S=ÅÜK× lXÛÃL͇½Å6#׈é²D–*·•6r=øà– äcog‡‡«µH¸/'™¿XXyõ‹ex_ÓÂF£N]M‰Rw,~û›×ŽÀÖˆBå æQI$ªhvÏGqýI’Ed ÃE:UN•+aîòú5wÞ  +òrÀ©aÌpN¸9†:„¦%,{"¢é 2ûÎ=âó ’ Ï –)8˜|*^#)c˜>½G;þŒO#%$¦±…Â1$1#%Œ60P?k jF6Ñ’#1à !>á…ÖQœlV³D‚¸`B-Š^š!‹uK®þû=L„!tX•*Øv`%âÁ¦(ê‘Þð9ºó:ù7‹ÌbU ªö«xæVç²@; ‰hLnUjÚ­ ô0`‰ånÐÿ)Çi‰q“HÝT x<´-šºÛ+ÚØ :Rp„žÕX?„ª©°¢d×U’ ^:ÝV­6©f@ç4Â>.dLÄ o›©Ãi7Hí_8àml6ÖQÇÃÇ&ÎùÃ"TcñÉ´f1W"J‚òŘFñ@P²¥tQ÷Ô-ïHn¢6ÿUè‹ý)!åŠè…»mw©åf9 ±†š–:ëYÈ#%ŽÈΊ9V#%6)¤þ͘Œz5 Qb¤"ƒ¥·¾Ï,テ >·C¶{Û^OZÊWׯE f FUíD HÉÚ„`±ªÒý¬Œ’ xݨÁ‰¨ÛYQ²Ŧ‹^üwÆwa}3îœc £)äÀ¬­R ±è§Lli‚Äxp5#k³–$½Z%}J#%Œ_ôoMvçgL·P–+…8—õC=÷n aaÈ™P@zæÄ¥µfs圶ر¶Úä€ÍúSÊ•®ûL¼q¼NQ×;o“ƒ«»#„„»IG¹(¸Ã_ïÚÿC^aœB#%yr†Þ•Y΃N%â–‹´‚Øi‰ I ”Àd"THˆAƒ®áoJÅŽ%‹å— YG!#)(Ò#`7ÀN¶OÁDƒci¥ÈÀ6¥*î>Ì4s8CEb2CMl†¯1„)#$ÈÂj ¹b9L݇Š6RC97¶#)Ç0^Y°Ò‘*%QPM0–†D)„±T2Þõ$(b‘a“ƒVê9vˆÌ¯îƒä›Ëj†‡Hçø™ƒ2J9Ë´$šôûÆ(É­°Ò¯mA3…PNaåcZèL‰¸ÇSŠ)ô‹Ììÿz'˜RBpy?3éO!o4¼;Ha†$9à-9†™ÞkCr™Œ£!W®7óåAÉe†0Xá„—¦ ¡Â#$÷”†¦eœN1½éC Ÿ¦‘8@¶¦®eÅÐ3ažxì¤ß? B:ÿM31þþ¼ÙöĺVõ3Ê"¥¼C @ãT¦fœ;{\æ ]7(áhC;Í´,w%î”'‚JnÙk=æZŒ¾?<7°¸jgËÉ÷>v6Ñð?2sZ'—Ø÷7çÛT­Ì­«tjL;ÀeBÌ]3€úÔÛg\Ë9¦Bú‡ÔbXŽ½7@øCàÞ½¡ uüx8ǘ¯|¼#%H#%Œ@0€ö›>Ž’v£çØgm5,u´PÈe1ÅDÐEƒžK@Lgá凜À EÍÜÁ¹J#%’ës14ÙÛ$‘ÌM4hCJ€#%#)š]u#A`}í;3@Se€&#$JyÌjعPº©˜¥AD7† L S#%ºrn8ÐE ¼Ð+<KŽHÒLíôÖBÎgÁ”c9ÇŽ1eûáä‹$ˆ$^2z3ûiµågš86Mö\žÍT¦òXËÍÅÑÌ¿Úy*#ƒð¸L´VÅNd÷ѤÃ)³¤ÉfeÅe`Îkn§öûb“##$‘PÓ²ÂpÑ<Ôö(Ÿ4Y$! RÑ[~…ÏÜI`m¢ªkÅ·6Õ±­µF­\µº(È‚’Ñ…#%°ù´{<»LæH@Î}C(oDð_QÎô#$6¥” TOÆ,¯G 2³ÄÈóžóŠB#)Q# F„JdÊYµ3L’™4Å&Ø´™P~Û©35i ´Ò¥ ”Ø”)5¦#_onPÉ­&,J%3k4)“)™#2#)4"¨¦%2Ãìî¢#%…#)D‘I2Y@jŒÉ‚Ѳ£E‚)„Q˜ÆLÓÛBeA“0Ë¥Fb#%((HÔÅ4R7ÏË“ÂìíØî¤GÈ™‚m@™¹øÍ­}æê'‡‡Ýž&d?°¦Íì:¥¡*¡)• ñ8yLUdXfbZTt匆§KhÃ~ÛdA0$™E´ô•Ã¡$Æ°wó5½è]æÑD$ü(<ŸÂƒ&íÌÝ…ÂÖî7+âä`³–Šm^»›3´$«”þ×¢8®(N}™˜Ò“÷“­‹Ÿ³çä×Ì/¶åáÃÐ6Øc†µŒt‰%†Àßâ*½ú-¿~E‰Cj4ÙY+J1I´Ë0 A‡3{Ê‹xU/wDS¶SO¹§úÌ<„χŠ¬°øóÂ1IL’¾Xå´ÕIÈ ¶Ë¿fþŠáµ¾Rö:…“Ññ)‡°‡U#,'k=?J||îåüÇ=N‚Á’áT‡:¢Ã+˜_#´1h3•è÷Ù¹uMé—VxSrÔ–0=vâ|[ê^f´BvXmZ¤j„‹D€©#"@0íc­áÓåP NRi§Øà|Všc®.³2¹â×8‡&¡ÓBîïÇDÄb;Æå·ÛÍçl;ôw‡b¥–w³éòÿJ+ÃMœbMhjè(I-·}½zÁ"2¶Æ”°¨"X(EÉQ`»ísn0f'¤f¸½¾š{é·§"—^eÈÖß“}¦½,3O²aÏã}èç6äô½fE‚éA.ˆ(_Žº~šïÂ÷¾nŸ¼'ÞŸ:—Û4TÆ4‡å§0ÜQ^`ÛÉ.„“n&Öƒ]J2š¹~T#)¥=O–ù®H{ß'IB·§!0Èò¿ ŒÛ Cò¯²YÐß2qª¢í §€ÆR{-PZ eµÞçeÝ#%•}Jvìm¼ –Û‘kC߶’ &Ôy‡Jf8#$Ÿ£Ó7Ø@ý=é<Ûy÷7gwB½ÊH C–ï:Ê·Ï£ ŽEã¨Xlì=<B pß.Õ¹Î÷ Xœ¤‘Ú‚»" mña¢ {l¤CÅSÉŠ€:ÀŠÇ¥D±…Á9mÆO6]Nx¦`•§‰R{z¼-è•DªYz#)Œ)¬ÃÒšø3xï#%m³®Í%|v;@È]À’#)Žn¸Œa,å¢ã‚“™ŽÓ»ºxù¾¡˜èajâ;|tíEtëù ÀÕ„ì+©· ò¯óCÔÝ9QúÉ}Õ_uÝžÖÛªƒG×Bi‘'ÖÑGjG ‘ìÌfjVprÕ¡ë£#¯$ŒoS3Z?ŽZ·ªçÛÃ6˸ÞÃ1»Î®™«›†XmœŽ×‚É 0Î&šeXYxqS4µš¥jPÃ1³ a™“¬“0¦Ö¦iš#%=›»Æ¬*Ð9Ç虚%rëÈÛÂÝå0'œ3kUó‚þ;®ü#$"ºÊ‡É ™ŒÕ‹­ñòò%‹"î¦[CÕ“2Ö˜ëµciª[=Ú•ñ»)ÞJÎoIÿaÓÑõ:¦ÊÝ8ƒ½8âáÇqÜ~^¥àÎÑ…ÆÆŒcW´m‚Fg*3#$gõ¸Za™C¶ècP>˜]ªaë}‰eÌTm…“‘0p \±•%¼ÁbQ•¡ó¡‰ÝÙÛO‘¢#%³Á½ ƒ¯0ùm4#%fágDÕ[ê6S’5n°Ò#)qçÆyÒkÕ™wB¬ufB¿+P™ 2h˜µ28Í›{#'[ë‹2úuiã&>8ÎØß@ðc—»lÉAÆgZã5OKK=§ç1Hf2ÒäŠû¹7¸!2é{틇½Î÷*FÁÚYÞ#ÙÕF–®²PâúSc]>Çu)ÔáQÆ€ÂMd}ýcçŸü&ÕÍë}Aßj!àv79ÓŠó‰ÄI•ÎvÁ—yZºÎª3£8¼bº¾jwÕl)$™íÙ¡C¡Í¶ÂªÜßlÞøŠ=5œg-÷ Ûbä‘Åž·’tõx’Ö4äNѨÇ7ÒY,mxHˆYëÄÇVÃeÂ7Š¨ª8þU¶áåêí½baÝävß$éöàÓlg0iŠ3÷q¶ÎíˆÑC툮)á뺔îôg3OÖ)3¦0’jc­Š† )E.äÍ [2„ÖI7;-s•’ZØnuÇXJ4“Òá¡êRºZˆàÓ ª0¡pÃ"õWª^|¨I"ÜßP`QèiªŽZ‡4ÝÌ“X8¥ûåÒê3…‡)¤d+z|Anš¸Šˆ«ÁX9}¢öLZl9”$ –1f5W<s’e¸ÒA\¼E:Ë)‡ŠfxÅ< ^To'a™w´’qIf›lëÑY¶YÊ`¬½F:ÈU ‘ÂãyÞëè¦K•¹µ•±³ÁÆ ¢WèG(å0ë‘Sðk4ä±²”מ¦ÕG8l¸ ØD# ,òCìí %Èíƒ~€âFšw̧]`€ÕÍļ&©·«WseS².k£ÀÍ‹ÉϽåQxâVhlñ]çg6u#%"Æví3Í®L²j6)e§4HlúipXlhyz壜½g)¶.[Mf0ÕÝL#o#%ÞÁä^`êk‹Lò=Bàst±h“4”uÓró„Ãr¦Dm¢.ýkr4Ç-›zÑ‚Pé”tÕ"0;½øÚÌý¹ö*.`WNŒk'ñ÷SÅèqΘŠšt’ EÉ0:UgáoE2¸v¼oO{†Jkqã1¶åÎ2ÃUŒm·ÂsVߧs2Í]7Û‚0•š°¸N$çSlê‘ìfo,Kݸúg:Õc+Ø5¼ˆŒ§’Ó$C×mW³[s&\Øõªcp}´vGC÷™5%oZo4crÙY©hëdöê¼|3Ž¸ÌáFW•¹póÌ©ˆ·ª;Áƶ}h¥„ƒÍYZŠNûTäø‹Ã¢¯Íêéßzc(±i¦,KnéKjh–ÅX#%bìpÍY©€Ë-M¸Ü·Ô´+£ÑIÃ$8ã57V#o‡§ŽRL©²±³8º×cX÷§#-š»Ê马ycÉUc2ÄòL ÝZ™­×[Ñ.¥*b†µJZh=²²Ãv#)sår®_¾@aß ÂΚ¦õ4l®\zϺb5æM‡Mb~!³:¼DJl†ü©vórdž2Q4úA&nÞåÔÎבÝA½•ñÙò3‹u" Ï5/kb¦ÈE9¯#$Åš™©ÊÀÒðÛU¤º9£Q–Nu1hº·¾©-Xlì¦<Æ<¯¼zcg·95§Æ4N" d,ãú’qœ(ÝV¶—jºã³X¡]ëBá$šŽÁnó€Zè…IÉÑó]5¢ú™9 ÖPdI¾èBPR½/KjãuÒ<–ž'Qn Ã»‹G¾ (˜€ðÅɤc†‹å‚‚ïÁ²‘Q±‹Ìá ÌP5ŽÁmÉ·ßEct„6 oàÍÄ'#%ēɇn2Ø[!|¨¤P,È£ŒU™ƒÆ,x€a3Û–šÕ'oJ#%P4y°§ƒÁãH`¹6 ¶H°ÜaF…¶JMª–#)éYf (Yb-f­›0§“I†S[½Õû7Il†WG))"Š—l£ÑPŠ¡Á¹DiÛH”1i¢Ý™­#%åp›)™K¢CÜæø ñ½rdNÏÈàØÔÊä~°au—…Ä.¸¼D‹LiM+ÙÂ&pL*€º2ƒ8ØΣ=s.S6¸²Æ4›N„É©±‡»:ÁçpçžtŒ™} qП”<>ë¹ÒN%q¦ç[´²ã,fuŠÐÆ_g¾y­æÇ9iX–Œbˆd>†jHx!´d}&›NôøY0+4 r÷ëGS[ë}¹YÑ,‚îŠÃ#%5™F°gx9ºšhÌ6u ™Æ3ÀÔã°F¬=Ù0ÄÖÜb(Ó{eb¡ÀŽUEVøEUÉNS!tŠ-œ2±€,|¦Lˆ,›Ažžaµ»·›å¾ŠÆø\Ñ\µâÛÙµsE0ɦ]`RàRS8à)æjI¹ÀäJ†‡#)†€-£‘É8s½>ýq eʱÖ4µ’ÿËPŒP"m4˜ù#-æaqvî­·ˆôHľñ¹ÑéäÚGTF†P‡CT6p–Ä@(Ìá‡#%á‚ hqßyL…àj1Y‰¥ëØÞ@5GÕ‰zX»BVò©HˆurŽ˜dDŒrºIÄé5 N<ʇ@èk©±L/’0âSEK—í5*$Œ`"`Öm5:Qž¼zˆ²‚T–”è)•Ì åÛ•4@›!¦[€fDØZo.˜&ôı#%Ra¡ƒž¨pkU(FÚÐ.H î5êÞ¢äÚ#)»¬8F¦Ð2.:Ža³0s˜:0°h”ŽbebXß{KÔw±˜mšÚ¸72#)fb]ÖÄg8P©,Ôð#$!À¨6weM¬™Œ™«5UýäµÕ«ùJ_â$‰*@17Ñ`&¥)„SÒ” 0ÿ.ŸÖqWêðosˆˆj_›mùî×EXÈÀÚ–©i#%ÇÐ?¼À1$êEu@ Й¶o;k¿­«ñ®”Ö¾¦W[ªìLi1­æ’K@Pt’Tƒ»-/èô†Éêìè~¶±3åNÙT2Ã>”w‰Ëd‡iÁÙG>kŠŒ'7mD‰ qÈ©k”[ŸPï#Slað ·h©b®ÇF⥥͎IiÅÓRk6•£kvÙ°AlÔÀPÖ ÐÒ!2L÷;ýŽ­°[,rCÃp„"ÁŽÌo¯PÏÀ<56–Ðýò€¨¼Ð—8Ér…"H$c¹Núõ¡TVãM†OÙd´ÄNØB $#r""P,f6áæˆÐØ9$ƒ¨—¤0ø{Ñ¡—»‚µ¡Tcç=D¢°ip‡·ÛÞŠb‡pûày@n‡†!Ö+óQUoÛ´H0G‹ HPWwwk³ºî̹ºéqcmÒþ¸Y ø» sQûMO<Ñ˨yãp#$°þ³˜D7I)›PS‚¡½€ñ?F#)"ô#$Â1Žtƒ˜ûˆ¼xõXdâE«PQ€Ž_qí¤R»J=¥Ÿ— 5.þ'ó™#)—TÚâý-…õàlÿYï÷°Ú<ñ0¬&MMvfÕ´Ç}‚ƒÁ’ĦdDìíCi"4š]²…ä|r ÍÁiM^̓\±Äp¤ Ðj$§ AF=#)àØ(ðÖ‘ÊÞѬ'hŽbäÁbF#$Œx~qçË\’‹;@J!¨ÑÚ~ r#$ýäSPù«åñ -[TÈÞIs#SÎzúýå~²Ö¥¡%¬mŠ5hÚlc ±šfT•¤Qd„#%2$B”ÂÑ> »Ô`xçYª|»D·RžÍ½]ëŠb¯èêvÅCéÈáê|ß(% Âè±n6ˆ,–"11Ž³XÞZ V£´‰`v´ B܃æÞ½-¬Âü)ÝE¯­ÚOå»'yw;J{Se2†OÛtZPS#Dµ~@íÓhŒ“¬I³¿—'*ÈVa’`AöÕE3Pj¹3tP[þ#Š‚°ÅŽ´"È£ÜB9zÖ®UÒ·Ö·²õoDm™J´dÆ-™JY±L¸Â´¸fh„hÆ#Ÿ¼ã)\KDˆs›ã#)Ÿž·nÄ#)wªbF0pѪ¦µA’i0`ë†^­a¬pÌÇÞCÓW{Ä´¡‹ž&A7¶¨·š^Î57šL¶íÇÀ».]¡¶A¨ã*¯%;joE+]ÝLÇ%‰Ü\"µ׉£¬†þ„`Kmf 1äÎ"ãÊŽÑ1¤Ò¯`ºD²b+®—ùç½îBx§„NӻĦä0cMŒe`JD˜S ˆ«î#%$ºà]iûbÚÊQXKÀßglrƒLÎ),1Çð€‡¯/*«UZzÄÌÖjOŽ"‰ƒl I&„`Òð&RWáP6šµMêl!y¬êjZÂ’¦kïr0µ´ëíq&¡8'7[‘§ËÛ¥ŒíIðφ%5†ì´Xh4Å›µ7¢±A”2—ª2˜°kh¶––v‡„OðØ%¤äÎØ"WUè™Ôéc=ÎuÁKNl°‡ns׊š5’š–ô`<ppù’2ñ9‹nŸsVZbÓ‹ýøN,‡[1ºmÇ=N $PŽ¦Æ«iS‚4ÙÄÁ™œ,”ïœ åðj.-£D"ehåd9È›U7E4¥šÃZç<+†¸AÆ xÔÔkGk"ju´Ú,ë–r· Ð„ û1A!,aAÑ!#).Ù#eŒBŠ1Û«±†!À#%\ÁK#%#$p[!##)aTÃ#)`¹”@Ì°8_zj– € I HY¹Â36q¹–€žýošŠÐ?Ä@¤ˆ»Åvn .^žÏ=9T%þÜÉŸz¹M8¤Qb{(=GùºPaù ë«#%®yøeCb¾4v6Épì#)ØËíµ¼×†€(ôÈ0‡á­Wœö¦&„³Põ™õ²EÉ $Œ‘Š/$ŠÃýV#$´mdª±jÒjÕ~Ų¤’$!CÜH|âYmq•U–èð™LK‘ùŸ õEJ¾ÃûßT¾œ_¾%’ÂÍZþø6óDëªwŽ«Fö¹”Æ«¹6Mìé§#%gy“LÉ•sS#£*T»§–Z̧XUŠl¦Â&Èd­±v5-YÖ®”¢&†ClC8MÒ.™{¼æmÁ鮊£’ÝJ™“I)Ò˜JQ‡§—jx+~—K\‘¢LSI”QåÃRlˆ›™.‡Ð8¤Føáæ¨D]Ê4c:µ:ßcŒuhÖ꼧¥sù–ÐØ¿ÌäÉÆòmN·äLÌ[űºÁH¤;,+§ÌR)ègC1ÒuĘ•pìÎݵfgiÕ·Šªo|V6Þ¦Ó\$×3’¾fŸl¥9¯¼¾1¶\d“%aÌdž9YàhQ´î›&ßg¬œžk“SÅÀ£D1ægîa`ø¡`»Ea¹9˜¾DzÙºR¡Aa%D" ÅŒDqX0õãì3„-fÖ°Ø9r¾ëJãÝâ¹^e5½—žW²­yyÑFºÜÖÓ­Ú³M-"·3 9%РJq‹"í†Y#)³±Ia†á×ÿ +8ô4Ã*ˆ•ª¾˜ù\º¶í c³tÛ9Öös®]L»Žºë‰×qKÊ&·Ji²u«´šÁ¹]×nî)4ZóºQ2Ó´÷uÞe¯![Œ¶ånÓQhÒU,ʳ<T‰¢›FÍH#%$‚Œ¦´Íd”™eclZš‘L×Sk¥ie-2kJeFZ¨Ó쾿<&¢¬Ê ™¶­ Qµš‰ Œ&òÞ¿1“È\ÔyÂÚhLÔ:˜€‡ÀØ@“ì²PD´MÆöÑ‚—bQ”T¨*¤ƒÇ!¥7Á|ÐMÄCÎvž»£,' „aØõiúR÷– 6S0Ì×Ømú2Z£ï"h;ÝíQ! IÝø [0‚ÀZM&p2ð‚Xwç¦f6¦·è4u=F-%#)6=S½æ÷§L’G€3ãÔÄ70ϲ[”àÇaúŸ¾Ê¯To^‘$V¿#)-êD.\ ƒo†fPV’Û9¼n©å”kÙŒ&E39þ™çPGña² ±¶Ÿ”]"X+©²Lãu²R“ª¸ZÔÊdWÂr$#)‚‚f‰7áÓãüœ²tv°í#À6ðàe }z.ºtvƒ¹“˜ü¤ôàÞûp^h½æ€RZ+V»KÄnQú¹˜,æÈ^q`Zd!æ €ckb$—fw?Cd6B]«{ƒìP¹«ààL7¯¹Hg\-˜ŽUmÿmá1£ÐÄ)ó);~‡wn°qc³`R×Ox?J?®@#)¤U"¬(øz>D+ öO3VñtNê²Ê(÷ß Š5õ€Ä¿ÙÅM *#)¤#$¿<È´LPPÉj""‹hÓ4J“ÞÝTD’Mã[\Ðf¢VIŠÞn«\šm6‰Ki6b©4Ë&šd¥c1M5 Leù[¬2Õ”ÈlÔ24Ä–Š6Õ+TÑ{ÜQ´ÖÊîéjïVë]«ã·bŠ˜Œ2ÛS$µ4Ú¢¬A©£6ßЭ·Uö9 $ÙXÙ+kl’DÛ5¶¾WjZ4f©&¦¬yçxf’Zm6Q™SVV[+|;kl¶Æ#Dx·E¦ËyÕ×É)U1B¹]YªMàºËx×TdÄÖК׀–(D +îpåø8«•5æ{q¥B°CF;÷p–#$ýege ÇvGËÜuJóiÓ®ñ6û¤ÐLct'X²L!·zú·E¸h%gÇĶZÑ‘ª êIh«”ç åõðÑ&­ð¾U­o›m>{¦jZZYš[EjRš©_g—_ä~ŽµùÛål„F¬Ò¶R­ð…h‚Œ‰¾S‰ 4•!Quª5å*ºlUcnXª5mwßí¥°`tª&tÙM¥#)&*DSMM\Õ¶,•ilÔlÅ,fdµ~;JèÂQ©IjSj´Û)–²VÍ)¢•&¯­nPÉ%¨Öm‰h&)F”¢hm6ÊSHš“4YL³E±Q•²ŠXÙ2&‘¡FÉVJ©JÁQR›*J&Ù6¢“l†,‘¶Q5F¬ÚiR$É‹$¤Âi’i’ŠYª›c¢J²$mE,Lµ“²«RÊÉ&”É©Kl³j¤­ªújµÝjÒ³kM¤³i,–úJÕtÙ³Z¥-­%¶¨µ|µy³T[y›m¶²–Å|*®kjD”TE I%ÿ4ò#%Üᆽä.˜»°µíx:’¶½°†[2wë2íuÃÇ@ý#%`=S»Uá„¢fxbn*Èl""w·m NºT_yí#%Ïw²QÆ„=ÞTÝ0Q>X8y¯Ü"³ #í‰åõá€yC|vGªX÷· rY¯Ïã»êÙ×]ضh ÛYA3’ìõ]•Mù”£v”<<‚*R»LK¥òK>SËŽ¡רhÃß›ìÁÙ×t¡_¼ÔbȲ,ÒÆ6†ÈhHôž‘ÌdqÙ$²ú^@À¥7ÍíÏתüSñ#$x,¯ÐZÁ<-ÑA`}@/‘©³\#%Ôs½l#%°L>Œñä× ý9*`ˆ¥¿7B SÇ>0:³)ø!ˆ¸ÊYeiúß?ÞØ¢E™ ‹1š"„$:|1z÷ä³<õ°Ç¡¾;3˳kÃËv;oªÞ»§¸¬C'~M=xæ#%&ýÝø5WÇÇݲÖñg¨D´Œ_J‡ÕRÀ`ü(Ñ-iû¥[¨c¥ ÷í8m²@¬’3¦õRiP:Ù;$$¿¤H­Ýú:ª9¶…¹MFmiœû#)£#%h©¡#% ËÚ—Îï(FÂí" Öæ«ú>—Öù÷mñÚpb¤]À#$±¶ÆªŒßj®ëªä=œ>[îÖµw˜ÎÌTA÷Hø{`ºS«Iî“Û’’¦ Ÿ7ÑÇ.g¾ÂHƒ#$Y6<JQéà(Ón‰H8†uì#%°×xž÷áöŽ$ÖÚõéZŸ3|§ùz6möŠ#$yfK¦ÿ»µt8uyÝ‹ëp<ÕÕß#%± ³ûPÑ9‘Eqȳ•¬œç\ 3€·OîþWªëºéÑW7"þ¾y'ñÞ|;s^¬F”+Ίºhó»å¿¢o\ßa&P¨’ŒkrÈÀFa… ¨ÁV*ô4 É&j¢Àm&1íÖ‚ ,¡5#S 4?Ü£ËjZ@ÍÓ‰0ç"­²2 vÚHFsba‘#%‰«+Ì”fGM±ªÑkDª¨,Š×K¤& ÐðH°Â¸ý†ø¥[ô©5b0k ±Òôï £Âg+Ѥ£À$}ê›le#$Òб™èÈì#$q#%‘ïC~ú Ì‹D0•¡1¦ª&*Å\,vÜøÀM°HbІ}±çÏVú}kͤìÀ™3`µÙÚ¤áɧoFܲKé/Ûbñoe¨ @AVÅBúf‡WI}Ú‡ñíVÐ0†¬î–"CÞÑó7(p€#$MŒ8‹*²üÌ-@÷æîòœù,`I$c)Ó4 mýÃð˜0ƒˆ«àPù¤I#)\‡\iª¦ùåç6…zm¨þN·‹¶ NlÓx e¨k/´œXgÏcªH¹P!Vúñä ¾yðûð÷îcÂfô#áÏØ©#)g3ÕéY›h–½VIƒ·À˜2õø;3l’vÓú‰cI?\(Á7zôï>(a™®„3U Õ‚ÂR È_Ó.N z³ÉÔ S#M)L7E– É#±!b„µ#$Ø4Õ;Ô{õY#%#%Âj"…ŽAQƒ2Z=ó8#%ö§kN¬ôë­UÈfTéÖ-Â!k–FÉ#)/jPxG"¹ÖQækX#%§£13Õ®ÔìH¡fŸ<Ðä©!aDáøQT$DMšæàÜßJÐíSõ¿dUA©çÄ1í³˜¨¤ùµ  ÏÄP)ç6åç3Þ¦}dA0I¡x6UÐ[ÝB¬SIDÐã¤Åõ 2­&‚¨1Ú‘m.Ïæ¾òáM¯yIeèkA—#)ò##$®|~‚6€CÀ¥46{Î$Q4†I´XW'»Š±5‹Oßçp¼ém<Zq«ŸÖj#F¢/…2h†7ÛCVˆíÈlùò5N‡;s÷bݺ28srYŒPÈá_NÈ™Újè5îËU†¡´Þ¶#$Lˆ`#$àwøÔ“—5V+ꢕx=eê,Èõw3»¬ÞVÌóT.4÷OLœC|‡3 .¥ØÆèÖæf¥ÚÔí]v®øìpEð]á‡ÈíM-H“ÀÀÊåŒÈªâż²Æ>õGvÒEhïTÙÐm(‰rI#)N##$($'N%”‘d‚ÅuªbÆbŠîC¨Ýw}låÇŧ 1½î>k£cVBI‰­6‡¬;©Ë·cgeÈr_U×]Ü‚_SKó÷ÒxÕ¢REM*u&ÌÀÁA¤•Y¥Ã½îIB&ní:á!GuàêèµÉJ\éh9<xÏà7¹ <®®æU9Ýùß-ç±ÞëºTF"pª?UC)l3@7–ò“õF#)Õñ0±áÄÅâžOZèwg¥áÊKQâùRÚ«­ðËröˆöÛl7cÝ99öV{Mì'#h6—Dk»\xÈØKã}ÃÆVÙ‘ÉÂ,‰°îÁ,~ÏRa=9‡#$8'#%¾>|ºDª#rø‡Âó£»Ù‘Ò“¹êÚ'W¸ÈCP=x"p/ŒaŽ(Ø0  ;ðQíER]SY2s9õñFm&wT0eÄâ\´®F zž,EIiïmEÝ×I_]‘!”HNóÊøÕ{«´›µmµô»kz‹ckWžÏå<];«©’®\Å/„ËÌÖóM×–æ­²Ë(F(k‰‰˜0> ò.CoVZ®¼3!ñúvñÎ* ÅC±#%#%,Iéá9õgr¥Æšá»«"vP1AÒ¹“oÓ=^ÒÌ‚ÍÁëêòèkp¯խ:4jȸoûìÁXsko‰¾“Êü­­½TVú6iŠÄ•U\ŒÛ,Òi²ºè¹PÎ릕ØÛ~vÛ‘"´V½¥ÛÕêömkÆå©fßÊþOxµ’µ¦Ù­­/R]϶óYp®h“ Úþ|Sše>üia2뵙ĆP- ²—C€ËN&3¦ÚéŠ{àb‘Éç{…Ìžƒ\Iã6Y-U€tÁX‡`—h‰‡÷cïÁ·dfqаj,#êÞË`ï7›Î#ë’Š†j¯÷wê-)C–ú*Ž’@<ÉUGÍTª2ÕSÒŠ#)@œÂæ—#)rVl5dóTy¸}ÞTbŸ§mU5¨¤H•E8´ÈÔi+bÓ¶­º«\>m{I±FWU— a¶V™3‹$æG›‘î¨p8mç#$ˆ$9Ä3 ¬"[ËÛ}ç¤&ݤß)Tå © ü’’"s,XÚSÈ2O¿oq‰–^}?õ°ÀEÚ經`d Œ–™ =ß?¸åœ’B#$#$¤#$J?Ë@oû-Š¸›úìû-Ý,gÒs&É!È„‚. óò4÷lÖ&&’/‰£‡ñây“å¤ú²Ýk]èŽ_÷Zù¢P3Jˆã´3檆üelXrÌ1¸y§ÜࢬวðîøˆàE@ôâcé¬8‡Pë*Å]¡C×9ãâd묆ýÝ!y´Ù¿ò|¯gPïh"3ï ‚ps6)–øSTSb/9–>å~3Ð…þ3¸‡_œCÌA$N ¤ªF¢#)AUE„#)Š•T†HïFŘ‚TP‘dH¤šHT¤Î¨z_qQ°»»ÑýAWµÅ[bêT2€b:‰B”gã#$ÜÈ"bvœÓ™ôØõ=j~O·hrا#)òSóBA‰¹<éëJ4zBrN&¨ò³‰Xÿ8œ9‡DŠ‚’#)@ì„;lí-î0ÇÜ’Õ"ËaH¨«$+ì;¾¶y'Rf³LŒ&8÷vi¬Ðh%èr–X%nxs‰tP?ié=S”k¿Wq@GP̳1^Ò€#)CÜ:»øž‘ûÖ0ˆwP;1a9ªFdüãSïæ #)~©TX&õ"ŸÂÃö5¦hþEû5©†:T‹BªšíÅ4T2H’©IK#$Icº™!—íCF,Š:×ü¦30è¨N-6 £D6rÅâÄ&Ö1NáuËÖ‰0Š±c›ãƒ @Ùh)/“͸ïz.´(h…2KH´PO¨4ÆÅ\Vi1Ò#%Œ j¬±20Ì®lV5#ÓÆ6«Cm;µ¹j6cë[ϧjõmoDKn47x‹bbo†SQ#]AÛ_-­Í· øo&«ÆÏŽ×΢A’b(Æìæœ0B ÎQ‹LÈè––°[(- :”n™W5š\¸Á¶EXVÖ¢ÄÞªÖE&]#%TË-8Ua,–"‹XÓH)±sj(Ù6åÍsQX×1&5çÏzôÈöëµ¾¢éo=ís8“;H-3†­:‚­ K¨2.ÿñõ˜i–4ß%Ã#šMeGR#%éøø|¢ßjù¼âð\XònÝÚêua¤Ä´œ’S›:] ¢¦´RIÆZ±3͸´ö>€dy°áàLÌßò¸ù€ù©m²O5I‚kªnt!cê{ä;âÑ3øÍÚ¦fÍ$ZɬƼv¶ªÔÒX¡‰ÓèÄø_ù¿¡ÏÉs·¾{Êlœ‘8#Ð4#) žØþ¡ù§ÙˆX„R5pEé#4ÿ4}ä­iÜTny[&¦ÌdÔY¶í¶2WW$ÉQ-Œ4V(¹«n¥cV1m¹»[ ‘ 1#) 0Fhš «#$bv:`nj ”T£Œ ý\3ôÏÂÉ¿ï@tngËðôñõ÷ciçMZ,`nqùý&kŠž:"HU¢©GöÑAhÁZb5LƒKº‹,€†K$"Q" k®¾¾ºl4JŠ:qêökMÛÅ#$ œ”ÜÄþ1ƒxª¨$ º«šMëW —ÔóM¬–ó®¦l³mÍsmâ×àV¼—®Ôlm“`Ö°Ù#%¶:V6+ªîØ«Ö #)½‰!zb ¤T¤‚¶j‚B4ÄÂ’kRmT™¦¯ž–±$4T(Aº$—(<ÉŠq®ó2±û9uC©¢àE­¬MÁgr£—$ŠHØWå5E˜2Càê#$€aP3 ”™ƒbL²,–0â¬-#$/}EA¦½RÉÁf#$!´#$/7ÊS RÀÀÄ/HpÑìtÞ8£”‰Ö6) XObûd‚†¡’%ÇŸq‘ãßNÂðM@DÂQï!D*ô‰UR&AWC;ìÙ% ƒ¥#)'£° Fî\I¬ã·…Ë(™†üxå\ÓϦ#)T?†ÂÊ#$(¿€|O§”MîW1 ª8tîØ`!ã$úÃȯXÈ[MIT ˆ)m)SÚ-C-5ó5Ú4Ö³JÊ{+¨ÚÄÍDQ$¨…üðtñç<ü»}]¢ŽPì·fûZ¥Š `mnŒŸOŒš§•³Õ”8èQùÇý~v ö£9·æÍ¥¨4¨aéíŒWšêàÖÛÓÎwgë•yL¼óÆ‹­ò‰­êÈo¾wÓ"tÚ¯y#`”PP]•Œ6ÀÞåA im•"p£è` rÁ“ˆQ&·‰*†ÀYˆâ=ë¿Ø žOgX ¨’D’f'DTGôFæ.'.íŒÛë¾Påó:‹ÐNfa!H@ÏzO ‚A;Q+BCl1š!7aJ(ÈB˜“üßo q“„“gGA7ï†õ3Ãu%\›9¦#õØàQ€à› ²'‡­JŽ–´ª*©ZQ¥Òé©NÖlÃBFk‚91o³mx6Â#%#)1ûÙ™&Å0Ù#%9$¹Š„›b JD3¾JR™Cî#$õiÇù²v?oq¤ôf†fâ»eŠ>Ž(q±é–9lQ³ýРøü¼mšãj'ü?&›|.ö%áršƒEζö™œµË3ÚQ•ŒªO¢²uuó§¤=Oµ4Íz+†¦*f“‚F¡Z¶0B(A‹Lkl ÃT””›4–ÙkMRlkFF3M~‰]&‰*#%K? m®j¦eSf6F3I´’¬Ú˜–-e[+6²”Õ“kÄZÅ¥lÚ™K5©¢J¥b­±´‘¤’HÅ"ÌÏÈóQù©„Oö?;IÇv²[ Ò3QÁS¦!Ãö¥€5$Kk©ÊÕFÔmQk©m«•»7ƒo0¨Áoêµ-z­uo¿P:‡™ÛÓ¿!#%ê ²b5©Q$E¨F2]KóêoŠÛõVoãÒ«íYü¥è$”WѶ‚.%PÆURت;¢T!cÉ sÝcŹ>% îÞò°0\#):÷m¼HV|Õ#$îÀSÍßýŽ}xÅd#$¢QÕî ôÜ'¥9gö±RýD„ zb–ˆ"T‰öÀ¹KÚ–é#$Šñ¡},ñ ÙÊñ-Œ£Ðð=ÙcýYí7mÖåì#$”W#)ÉÄFy³ÊêVšÛ< ,Ö2¶ÓKÉ“'°ý²¸ ƒ—ä[ 2à–Bn>8Ûðõ*žr'`ò0d‹â3¨h…pvª›Q3Oy°»½x==@dï î IY °‹ aP&­É<yIc_pqCƒˆJGÝø®˜rÏñ½ØO8ŠÖ0† «#% $ŽâPþ <ÃGNB¦ 0¤ Þñ?G¸ì,ù¢l,{Pûÿãûã–•ŸN³k#%4GUÔ/ѯ¶)údû{Aé¸Ëj6JÝÄ㌢u1%z¶Œ‚$ˆ`é‹cþžGF+D/âׇñ»Cñ^m<è„!âµÈÚe]ͶÉ3’_oÜh¡¶ êó¾-ApÐHã0´EM4ÛÈB û0‰¤ –|<#%1×jÔ>Nòý.’(d($EQ '‚VW-qŒ Ÿ³—­ò¹½ 9>oôÌhÔ#$~ŽáHå)R¥,¦üÔZúu¾|óÎçi‘qº#)¡,¢‘‹íöÐcÒ—ñ³ DP8#†¡¼ÜÕ†c€ÛexÀÆV#$0`"BaKSrÂbB&²Z°ø\73±íèÞo˜lip[}[h —6ÉiCj11 C§#)gï(ÒDL6јÂD‰jP1Ê€#%à žó!vBèïøçËú¿&×n{KÕ%‚å3‘«…ïaÖóxÐÚäÊ„&Ö#%G£Ã±sxFÊ®ÉØuhA × 6mÔÍ/–o&ˆQOñŒïÓ¤% î›(XÂ(xÐ4E,„BD¦„ƒ)8´p(EêDñ ¢z<¼)y\UG!Ôîñ2P1êæ‡Âr󇧶¨X²,ÐЪ`T¶oša-]Ôž´üS.ÐÊ ç²ÊhmÐ,‘¨%`÷tÑè1Ü?PÌÞà®øöÃ,È€ö’CÌ!í&C6ä#)ˆD@YhR# ÆSQA†ØFþ™ø>2_}m÷ÛQA_™ºU+ÍÛ[¨­úÞW…ñ¾heãÇI¼¹’HJ»¸¬éHˆÁÅTU-$äé¨_* Š¸‘Ãè*…"­7k *#%¦â7¥ÚUŒß2Í0dÍÖÕµ­¤âqrcš¦\RA„%G¢#tÃìż8اfÓ½]€ÊƃXcÙâw4ÎÆZgwöÝÄ5»¹U÷£è¤_vUcŽ††Ú 14®\¨#‘ l6õ¥„æo!©€Ÿ$‚Dœ”/ïHp¿>ãŠÝ!b=÷;’Ÿ†þÞrk#%Â_SýsÕžCl ÁãÀ '½²æàÈ[FR(ÚbÛ&®Jª0 uV- £ºÜLî®Ê¨DÄ•Êæxè\hDM¸åFx|3!ÍM¢pÑWDó×(£W—“9©×..NÏ8#%Ÿi é)bSõçnê0†ßO#)ÔêrÊtq/–÷†6äOˆ…§WóU¦®äLÙÌÓL|3’øàÅË ÏR¨œíÅòÖ³¨%®¿Ùý©ë·íë–r•:è ¶lo.7ª¡X$QnÁ¢† RZK-…²å£ Aæa€6U¸¥Ž”È)ì"¶"¦ŠÍþ‰Õõ"+ÈÞmÝCàAÝþM v`­@&>¬=šmÉàt`Øé`¡‰¿«Ò\8Á„N J:éܨC8§^Ì#)Ì($«U‰`ͽ­‚¨ï(ÙCGåË£—Ÿ@2Û´êÏ"l í·š!Ðû<ÎO2'£»XÞCO3Z®Ý{Ûz`K"[KÎÜDal… Ø¥q"!·b#$Lb@ hÄÈèÁ¸Ò R"*©T!`¡#) $>?73™ÐQAl Gûi½•ªAßÄA°&Ô›£jeUT@HO¬þ«lþßzÀ­ŒCÅhŸa+Zi,j±kP‰¡ös=å¯áÔ)·w¶õÔ¬cº.™p¸±ªÛ@‡ÚŒ¦y—Š2m¯.w6M}cÖ·¯€œÝ­d›q ŒN§+#%b#dd1ÖŽÐ\Ô-4ÄÑË1éBkTÅY¨¬!²«›dÓÈK¼¦y¶FÖ (¤$™8 pÖ!—jÐ#Ca†·ÃÐn,ªˆW!wD 0ðb¶[0 eö¯¢#$ÿ!ÁÂø™„„Œ#©T2ÛR4X7©Û¿Ê±• Yü¤péóU6u,<ÞaÁ‘Ý«éBöàM“þ÷cÖÈiŽ'D÷/Tôõ B0 $ˆHã#)b‘e¤­™hÙ-%[¥5_¡_¥·ëµ¤Êë¶ÙÝÌ[’Ô#)Q „CéëNÀÒƒäÜ1ù˜s…–B‹›¹¶©ãñ[€E‡—e¬Õ¨{fáÔqá¸ë2öc#$W"Ÿצ{Ê:š?†'R&A.ìèskÒœ Þê<³ˆUåŸôZï­o6Ú58U~÷è²- ²ó|'ÂÃRy-o£5¸Úa$Ù6UoØò™PÐþÛ×Òc†þó{¾2}H`¯”M@æ ´¥ê¢6`C°ðÌ:æƒ./‰dÏFg¤É„‰!2dÛcéÓs±‹g¢³|Sòf²f̓Xf­0ìnd«#%‚VÜÓ~·#$Å´S-3JjÑäJøKºÆ­žœùHÉ ø¼±ÜÛ‹n¿œÎÔp9ëà–«°ÉÒ.ÞGj¦4&zýrøŸÝ뇣ma±/ŸìŽaSYëÌíðõâyÄ1àŠ³ŒáqÙ´ôöýW›'c#)‡™¨Rß\ °4bbU™B˜æ+ÌJn.tþTØÌúQÉðLikâ†'XÏkW®HâuYaÔSZ·°òü­#%¥÷ÁYM]¹úŒa;ê¢IîI3“§ëÆüç_^ÎOr—»RýbPëð5d†jØŽ¬§›”BáEA4¶tCCsþÍsíØ4d³»\ÀçÑ}0OF#Ê[ã‹és—µ‰ ³©ÚÑF±‰Ø#%5"+URT‹ˆ¿qRP!Ø/žøîå´#$A~GtºOúÿ¤¿,+(ˆ ධº%ÄŒ›º²Ø\º¶®éGƒ]Yçnçnçwµ+mL­oU«¦ÛWîM¬•ë_©&Ä!41¥BûMë‘s¸ééätãàzˆó8¹(’L>oßþ| ;p€äš´¶t’¤4€#ìÚžü’ ñèÙË–MÓ7Íys í®=l ½Äìsn[ˆ9à"mQ$ sÂý¬+4æhë]["tÉ3‡Bƒ(u#)cE-âj<„–«b/Ôlweõ0` í¿fÄ!lAÔý¢uü¸š#,vú~BDV_Ïa‡`568÷Câö-QU ÉŠ”œŠ·kòÞ5uT6îµuËrì[«¬µš[M-hµ))fm­™j®«cEr™x¹&•Ö­öKB$# ŒQFDE‘A9ØÎl#)x ø?WÜÐÓ_DS®©–Ù‘ÃRØ ¿™ dÛ»à–NÐùTlmp0q¾¾ûVc…„Çð  Ìý< Ànj‚˜ârÚ¡–oÍ 5P¦o°øDFŸhˆ24Ž2$t3i0Æ°°à=9ý-YØÄŠY )AŒiò㾃§óî}vÁQÃc÷“nÒŒM¹®7ì#%Áu,ëÁî7)‹+X6h^¦oöWY·QïD §ë¯1°C/J1n‘âtzv-¼ÚÊøü,3<"#$iD—7#%cH:V•µ‹t¼õ+{ã âV#1Er•¾Õ#)OÓt“\˜Æõk¨ŒÀÇêMb“d`ÛúËæÂ!±…™¿lOù?Ëþú_î7ëP§ó!&R¤â¨Ö9³æ¹”‹âÅ‚F#)JÒDøvÒZ¤(M¢9ã¥][¹”4”…H­±0¼d#)CôšËÞÐètÉ£ôµ.Ǫj™HW’6Þƒ`#%H(3€Ìv-8x$Å;`RÁñ“¡Ã–×$PÂÜûÛ¡‹G‘"`÷½#%‡Nˆ¡’Ëô¾IÇ‹äGMé«c–äP5]Ô@}ÿ4ÿˆYäýÛ) $y”pùUÚþÇ®8èg*«ß&ᤎ§f¸#%äxñëW»ƒù_¢ÈÖñp™³ ÍÕ˜o“…ñ =Lo¦#$ö ¡UïÌ«<ž6i´ؽ¶#%Çðb0ˆÃ¼;àÎåéÒ(íÒi›€¡æ]&#×ÉI…ò#)>ê¬êIœëPù²– zr\Ckùƒënwç²Q—d§÷Ö #)n¬Æ:×i¿LPt5;ÂCPÂ)´ÏU†ŒÛ©™¦©²[Êcî­ŠÑç½JèXÊ8ýO»CbXFä™a¹÷¬#̽BΚ>}#°åéXj3û#!CrjxìíÔÖ08Ø+KV{ÜLèO53ÍŸ ô™á€†nEåòݾz¿£1HŸ€‚1 à„•3; döt! #$˧[¦qŠ¤’ÔT‰Æ,–X¶¨µmFÛli-E©–Ô›XÚÕã–ª6×-Vû)Œ.ÇSC»èÓ—EÊåQ¤¹–¤±P#)„?6rªöGq±$ƒÍÓ´àv6ßvþyab& ™<Ì|L‡_ÌÔ f*‰#%Už}¸}'6˜(eõQ·fØMj~‹ðóã>Šcâ×M] ÈoåµM¶H0I#%±¦7C·ZAŽŽa8w'åæSÆ@Ùâz2¿Ùûý` ?D@ª¦‡_>ýÎ#$ÍÔ(”“¡AÌÀ‡xRW€ßO,ÓÀº?vƒ»_¨LÆ‘î8ØD ÜÀæûÜæ'²ăõ3Ž]ûi¾¢1Ô=4=•¸¨ wÄ0ÌV1yɳ”dH{³wÅ‘KB}éªm»J-ÿ÷¶Ú`”&ùÔ‘X|‡Ù z‚r¹6gá#$û ‚“Õ¹#p>Û_&ãßÝW¯q»‘zÊ)#%)¨9D¼@è€<-ÓT€kã1°¾Ž· Ò¢ GGFÈDY?Ò!h(îNçA8Öý]-{¥ë.„6¢ [n#9†ðÄŒCdPÁ4RÕ#)€ç¼=þ2Üác¾W_n§ìµBV‹—HÝŠÐPÊ®îÛ®.ìÞëê ¦2ZßE¬ù3èŸN³ ã-O%j½þCèÙ£Õé6ù$|E¯¬í—¡öÓáù2d|•´±<gíýø\6x‘KßdÚÙ§ZpÈïÜÎ^1ç\K€ æ~»!±qÀ±aMU<ˆô©2˯môŸñÛNºíÔó}~Ñ>¿€^û¸#% ÀótM¼: (¯Qí ®Ï^ÂòßÆm¡ò×#)Uy¥¶Â‘U„Êæ¶Õb#H´0~C€#8e+ÒM&]LŽ’—DJ¨‘ÅP Š#%j¶¬ŠBŠIÁA¦’$&›R™›FÂÆ£uÝEz uKùžñà"Âë lÛ,بZ18hÀdh–Læ†Ê`”Ð1SKC›»#Nn$“0U5$2H¢cZh%`~jA¤067ï͈Y=?l¶%]à)‚I´j¶Mt«›ì•ÑfÉ«IW:IyÝ»¶TšXÙUÂìKë&Éœ°B1`âT(HªYE&j…™Î!l‘ˆ1Q”ÒË=I^6ò¥¦Ê™LP#%F¦ÖR±,3LZfÙ$™´Ø¦³2L5²–—½Ûۭɧw9·%E7]\«”í×|]x×›Îð½»\¤Tª‹zö÷³a“LÔ<¿Œ^qóÛä¼ÉqT UÌÞ˜(fÓK0 ˆÅZx9³YV€A nºG‰éêÌcuÛ¤Ò&Óµ6;•VŽ#%¾ÊŠñ„‹#Jíq[DŸ.<˜g‹o`ØP7ÜŒ4×b¦M¡»âe,ÚAí³°© ²EX¶¯mt‹·ÓºVjK_Et™ 4æÌË%ªÅ¶ó‹¤l”ÐŒJEÊÐP2 ÁŠÖnd‰F;\4d2RP3f´ŒB‘X¬4Ô*Q˜b’#Q±4 oqm,cJÙi-+BA)#)ês6ÁŒlI» ˜ÐÆf;jc ß».q Â5àb·!YY…¶ dâÒ«¡×#Ya ‰¥Z &LW=Ù¶²C$_S#Ó>ûL“aB4SØà£Ö‚&ì¡S<¹Ë†ßjjá4:éTWGˆ'ÊÚ™±˜ÃÓÒcz@­‹ºáT¬)™5;›f[:¢á¤´ñ1 -bÓ6ÞÞªƒ¢òÄ*ÀÀx3éÈ‘ò5Î#%F.ÂýÆê#%šnX¯h‘Ÿsù0|äøžKeÎÓb6LÌ4Ć…·§1$Ê æªWz%I]Ä#%¥¶äDdbŒå‹$1è!f•O#1E“#l3&m’XªZi½d[Öõ ¡G·— ´&µ¥Y¶iSYpŠÇ-Š”/’ÔѾÁÃÁÆѾ7möötÖpÀdz m0¡ºe×zU¸JEï#%3¡¥¦!ê$åJÜ,eeU»côt!;ãÞ!`…Á#)"¤ÀÂÒˆ‚ Hij !° Ð&•" Àa€LAŠ#%Œ ÑHQ,F#%’Š¥Š.ä˜%ÈI.%þ ˆÙ¿‡OQç¨êl#$kÓóã=—j]›§êx3³qõ÷5‚Õ‡û±bÂe4~©.0¦*àû>Lh™5ë[ƒÖ¾·J¨MŸánšÒ[~Ÿ×¹âí…μZóÎÍ"ooOLzÅsÝ4<5¢àݶèÓo=ûº™)doowIÙœ„+=uG«8‹¬ºÖjcfgME,«8ÕÅ™)ÃĽþ‰¥dõë]ëöÎïc𠌼@„9Qy·'Q³yÑ]sÇ™· 0ΟDð›ìldêïß3\p6´°+ C“)ßpxÜ2©´°›"Hº®mÊ·{óO†í¹ûûŒA¿=EÑA¢vÚÁ$yy«@Í4bK~²‹è}xb|¡ÇYR:{ŸŸVvuè›÷"åÃÀ{³P’X§„ç#$ïŒ<ৢf›²¢©–€[l¹$‚Bļ.ˆ«µ² j€6… 9Ø&n}”Rô#)‘±³˜ï±m_u8eh&—®8‡ú’@yÂ"yþ™‡—8GÎ[Å$$,m„ª[}uü;Q¶Ö|Lºo,ÒÜÍ>xÈ š6D¾•VÈš6%ËÁ¾Z£’HE#i#%Õ]ZñÒÆñ­Ù«p¶Ü¶ÉUÞ:6*Ư:åÓWVÜ«»s!—/QN¦’Ťh¤ceM¹Â§vÚ5IYTÞ¶Ûů6´›¦µÛuçž1hÆÖÕ‰6€À;#†DV-™WÆÅF7^™c"“>NgÇ ¬‹i$V#%#$™D"…E… ÓX%¬ O<‘Bê\#)B.øÙa˜”¬—Isr´×Æד4Konµ^Ô …`CÞòß&Y¬VR5$D2íQ-oº™ªIüÇUÙElVA2”±¨Ö4TÈÔZ*-¢±Ze`±ˆ¦Tl›e$ÌeS`©™,cUM^íÐHq©¤#%Ç·§Cቊ|zþ#%BHÍÝ÷µV½øƒIm¡4d¨eS¼OŽÇjÜ™#ßÌàáýÛ”0¨ê Ä¡ÃeLi´qzûN0PDŒ€š:ÒS-Eù—<”Ùbªý[òµ­Â­xÚ麨¦yºèÕ;»tµÝ¤¥¶¹m£&±[º»JV´Ö²½íÙ¬©Ab€ ÉRŠŸÜ§`qÅOÐ1:#$~{)_²#%!¬C˜š°&1TP#Å!¡UÛ€#i$€H($s£{jRÜutp h”ƒ…Ò»°§0Cl¡‘pé꧓Eã!ªÚ8yÃÔ\J~Òo=Ý•ö<@öà£u0}2/ÞùQXA Ä» ªÑ„È‹éšnBæ;Ä|7pDÿ®¸™·7²™˜Ìn~[)$Æ2»™&š„Á4‘3ifÔÓ¤‚M0Ì–çjmöv™MÊ[]ãí+™ú÷8òï9„›YxLh&!Ð&;dé¶*ßÑ=˜ãTî°…#%l‡2}¥t—#%˜U{FK%j¼¿W®¸9ÂérAs߃f›‡É„Ê´²ŽxÎMË°²Ø®‰h¨8¢iÜhp³9¢¡†˜—w³ÜðtX*þòvЬDBt`,:ÎÌvîVCêì7^W±‘Sj›'-Ÿ³OËƇ÷V=0TLF>=f’åLáZUI ½dï|È|Âê Ì2Tãø@FAQP„W@‡DB "°Pÿ@Ïë4ÅI ¢–:º­äžº‹dÚÖ5hÛrÖ×€äqóŠ ûBXýáÔ9ÇÃÃ’2ý0$#$b@ ÇeÌ}Å!ñgGîLÞ` P…JÉm5×gÇ-´¦ÛtÛ^Äñ(#r4Dh‰xa"àÚ´4}ïf¶Ðˆ6IK#$J`•o¼M¶Öd˜ÅU^,ÛÕî*¼î®Äø]ãk©˘µŠ¨¼¥ï#)±²¬bŒ¬HáY¼Â±æ“µ²3(€¦`Ûe£lÕ©J˜ãih"«#%aF3ÆÐ40¡Pˆ‹*-lX#%/!•°“0ŠŸº%f!L"\˜€¼h>AüwŒ[i±8ÓH‚#)"0õ’Êy?—_)P¼æð”…âÏWD?â}’O«ÖÐa`ªsë> Þ!*ÆYõ,]‘³nÝM³ÄÁ7cÌ5%¨Û·?fü·fÐuôeU‘; 1’BB#$ݨ Š¦(@Còõ½ùcóûÉs C變†ßø‡|4ÃûàÀú!’`ücê—7côÿ@g”Ïð04ÚŠ¤¡X÷;À,Cvúï¹÷ËãN`ÇÝ#J&}¿–ËWYÏ›‡ñ:hò7züŒë}|vvØÿCV¢µUÍ 'Û}ìèúQ40¹Û¾q½Ñ:Ý$>“L‹pP#%í€Ä«#%4ØϘâ›Û#$HgÞ„ÐqÅë²6Öó;Ï\m6¢¬Ö¨©ji-8  ¶.‘JPô)`¶ 2H¢´0Z¥!²âØ ñû•?hA]"®EykÊvNøuû:‚KKä’_Ïš]¾'á¬Ù›³–C¡Ó:é|UàZàsM¯mUªËšÍƒz|Ý#$×àê?¹ÒDª©EAJ‚%R0h ÒÒ†aD,ÿ]Îê#)¨•#$1{S ¸ÿ‚Ï…Çx!²7*B-P%²HÀZˆB„_˜\°'Cò㊠$Æ‘ª—‹»Îë#.Up¼]G—w‹ÈlJrÜËÍÆÓe“I5 ¥5G·jm’±Rl`ñº.mͨÎÓy¼»q×]³è\®îÝ"¹x·%QÊio'“]§5™c@íæÛymDVÑY*±¶‹)ciå¹K3IRVy×t—NmÝ™ÓrE*w]ŽUÖ\ë66Ûb×j[sÔ¿ÂêÖßvò—U_Rz`#%† °/^ßFð|öC°·a 6r£öy¨€;”þ®ä-F C¾Š ”'k,ª/¬ÄDN)ӊꜧ´; ØCÐR#%ÄøÈ #)ñÉôǯÂÔW¢û#%žÛ_ fÂöyòîŸe—ÝmÉøöͧQìÕ涷ܖÚ5™€¶¨Êfhg"Ân|ZVYA`bÿ`ŸÑF0PÎtûxØL B$ˆ°ˆbªÐ¥¡–|µ|Í~ÍûMïÙv¯ÙªÒ×Õ˶4–›Të®î¥•‹›º×³[¤¯Ö÷y‰š‡ð «¹ b8hG§àA 9¶Fn”ó¶ßI1ïtÉ’J#%$ÇbQILL-˜áƒ(‹ñzÖÛɶºínÚlûySâ]umÚ·d¤l0Ð\4:g³~õÌL’„ø*%gLŸÝÀõãbPÃÅ`”`{Ûû&‡¦°#%¨ó#%ܪ*ïˆ,€DR 6mYMªU´«,´ß~‹À vìù Üé €¦¹ˆè«„ jFËI3TkmCmQWG‰àÀ7¡¨GqñÓ$ Œ0•kûòÚŒ­#%jš—·µñ÷@€ž9ÍmL÷W¬]ˆz"$§(©­ÊÍ_êøD‡pzF¡&ÐÐÚz}¼ïª;“qÓèî÷mÝùî‰ñ´‘ˆrB!§àKÞþÙùmÊðr^(ÁXq„±ž„#%0pl‰8z-‚¿—š.µ#%äE`*͘Ȥé{â÷ båpþ€@cÈõôE·SØ(aþÛM>]£@R’`€2H–:É-CJ‰Ç„"@äTW‡n›Xø{íz¼«Igö¡ZDh Æ0#hM£{€¨ìZ„%D!#$ „Xó›e46<Çåw•åoµ‹Å«š/r¤¥&HÀ!hKAÀT‘¿'VÅUwˆ"¦gF £à­'¸ÕâÇvvUX7ÞÖˆ²L ©"ã#R\¶7‚6ªª3ŸÐK&YdɜЬ…Ô‹¶)JU†±QŠ©D#$¥I YT°©TT nòâ™DM#%˜¦X‚r#$5)7nÜ›á4qû¬#øŽûµ#$¤ó͉ãXôC{ñù‹Âð£IdˆUP Œ¯ÍPP¬& ”j$!¸ À]/c?*v0ÜöC&½´œI"¡Ä!ÚNä'4”RHÕDU"J!PªŸº)ÐDü" œ¨+ê‹2W^ÚvÊ×-Wš#%‘¨T„VX‘Š-!3#$gŒŸ¡ù¢Î¬>g„Á!—æSþDM¯ù}Òöl¹–[Zm,M.ú¥_c[_NßWœ¿Lóºõ¯uKº Ú¦¸ºÈ6ÛrK&8(BIjº5GLÆ슕ÒÙÅÿCPÛ4´(«rx;¿è•VN#%œ5#)²òªÛmW»LW÷N…èxPé¦x#Ô£â!¬"xZ·†Ó3´<„sÀ¨'éÌO¦;¸‡Œ¨¶~j Î ›à¢n‘ü`¬Š#$ ꡦ +H¢í«P@æ¨õ„#)'TÉgç®›[÷À-ö(u]¼S[Û¼‡OëáûAÔðÐüF ¤,2?ŸÖ¿q­dû·—ç(™(!£ðS@·é‰ ßóô•ê+íõÕëƒ?"· PhžúØßòÁfÍPQ—áÆ ÷¢‰#)2&Óóc<Í$äÙ%á¥Ñ}GÛ ÛAä¯ó¨^Nã[{ð…—£ÂùTÊK*êÎc8sæU¹ÍÑŒ´Òk¥ç‹#%‚dgB©3(zk±•”’ú™u(2‚¸©R +†AdÃ6ŒH–Ÿ/“½;Á]ÆýÍŠ%ùAû>Á·yÃÔ@ú‚j™fûž ÓÛèkÎþ±§•÷ÉPÑŸ[ú^ñ^#Íg¬_–=×Å'6s UF¤“ÃðìŽUTæÇ2KÙóNBGí}Uþ뱆aóf8” ß>s%µœW½–üûKCz4È Á"Š ­Ì]³—ëÕË—‰$¹Õøê”$±>ÌÅ£#)µ›ÄciiŒÄÑU5£ìg‚O#%\†ŸÝ0¹~/dëüsŽÐ4J:q‡YÛXòˆË`L§«wF}¤ësKáüo"‹`±o4¾gb·¦ xq=ë@}®ÍH%cæÉÛfõß|ëA.H4mLû¹Þ<»BêCôÕRn°Ê¾güUWÇÁ™JZAZ\¯[ÞšâOŠÞ­÷2 ƒIPöÖþѤA°^~Ūuæ¡éå¼ÿ}”ÖIy#¼Z;k“‚ÊáÄŒ ¤ºØ±ìlXˆKz`„P£h˜÷ôÐ#%(*HU°=s²«.Oa4Ýî/i0UhP·`‹A°Ìª ä÷õtôíÏ %O‰g¢]P¹g ·³Ö8 î8‡aDéCdC§:JÆ%z‰$F+å¼ó7~Ô˜CQ°;güÞš–.Š!hq,ÀhOiçO¨ˆÀ¨H¨”˜A´ÄTÍvœÊ:wuìA@Ú2=oYo.¢!BPÿÈ£åT¬ü‹ ›kR ík„àjHRI5_Íò»#%@Ýû²×6ý·ŒzÖ®[^M’ÛF‚9ˆƒKÛ$ŒâÙš»z5JX×ÁÉýE½¹B‰ jjšhêB"ü±åªµ$ìñÉî‹ÅS½ËÅ…=¥<5éÕ–V¼XÎPÚ<L€É;ÕKÙƾ%‹ž½/ŽÇ"e=¡eåe¯bjIžaˆU±´Q¼T¢ÈÇ© àΚ“Â)ùëõæu†òÈù»–™ëš¬—ƒ„gÄ›¦–î‡Ð>ø'•NçÊOw*4Íh¹j2T'+óÛ¸tN#)¨53lw®±($^ ̲™w÷ìmƒ]ðÓ MO&ƒÆ+¤!è|vU]ÏN~Þ¸Þ ×¯,AÈ| ¢iÂí ¥›\P„Ä»(#$O¶€˜ Î'gŸøý¿“0¬']O®PÚ¾ v H$‚–Š²HŠu©Ø÷£ÐöÃàü¢°nö‘QBA DŠB‡nÒõp]€>û¤z«æ>ÚódÄgQº’ÁCoW Ç 5=§Ñ‡Ö{S²\ùyÍÌ4 p ‰‰¸Q#Øsâ.;~;C§¬úÌÂǺo0íÔáâá_•B¼¾v’R¦9Ý«jÛçÐxim¦×ädÒ"¼Š:È9CÝAðIÝ"xBEËOÕ^î®=Œ&]œ”@õtQæÚrQpQÊÈ(ã…ís1QßxÐpÁcä žö%#$¤"$:”;~(#)H"„"vÄX¤b9ñ¨g[¼-±€žÍÎ:‰5B£«ýN#%!;RЬLcJB@s…Æ-kìãûß×ñfVXâ÷CùJcEíR6ö²µfv©#$Ää>Ý»<EJªäH#$Ðxƒ¾4Þ{ip'›¦Ü0y~“q†¦ËªÑÂ#%·ïmþkóSA¬ÌûÝ3:1CG3æCq™¢#%}QAÒ\ç#"éLfeæ*Éh÷E¤¸_g8[é]•¾dfD­#$zƒ!væoÈøËpij{Qˆ©ÅÌ0@ßÏž0zá½öÒQÑF­¨˜Òo. kÖÈ;±fTLI9–‰5DKœ°_«Xàˆ0‰2B|mþ?£mmCAË4{s 2? GÝùòð¿³Ê¢¨ª'×(³jlÂU9©#™ûrë˜îO~ýúoXʪqÁ‚Ã~ý`²=3¶[^èBöG/#)ÅÄùè¾wc´°ªªÖà ¾Ùr/Ý#%ê’;°¦/”` 0Öˆº{0:Q BC˜Y@q líO×D#~½k!ԶX¤ó¥&™Rgn9Æ°H¢I§NNêåk¹û÷j¤£XÆÚËe4¶lر-jQª›?&í–CsÊ”/¹…Åbaۗèù KU >¤ƒ*”ŠAd¿ðã1~ÿiw…&É™²DTÀi¢‚RÑ(–’$Å‚#%)°É±4) 6l›(HÌ-o¿}z­$#)%ä‚S”Ä¢r”·8ÁùVyppšR’u1 …NÆa³”ØpˆFÓˆÎ5‹›Ç< ²aÛKå,Ú§¾¬ë·Ø|å÷MåDf¹ÓJµ/¨N» CaÄL„ñ^ªˆ¦UY¨¥ÍK%ÚÖGî÷eï£i¦A?VîÒ€HºëÝÎó/}M³á“bãƒ^°ÂíØw³yíPèÈÀñÜ9«PÑÓZ+/JÄ#%ø˜'BÒNŽEZ¸Ð~sòêÒó*WÈN!'Ýüa!ø¢±1rTlÁ!­¨ñªÚþi&ÁõÚ¹Õ\pJƒRi¹#$Iüˆˆ0µ–ÁTÇ)O3›rêåom/%…¼&¦Á2Ìë{HFj „dÀ#Á¨Ð(1‘¨@£»)(Ûi%#%•ƒÈ*%+lË ›hÆ@Ð#$±€Ø,aX‘<4A#$ÌPÁ¤B¢TƒaSi,jþ¹+µF¯:íi•r®\È219%œ5ôæ€|›]Ù•tRè5J å¥˜™—HG #ƒžÌY˜6XÞE|š‰FŒä­c*ÿF½Þó].C<dâÆ. fšÀé<e@Ê*9“sGIö±a½Jw©£4¬*ÄqCE#q¸tSŠhid²a ÚÍMM …Ýh¨B‹!hIˆ#fG!fjƒ)EÕ,Eë•%#IƒpŠ°^ÆÞI £§®ÕlFah ƒL!ÙB´.¢œÑT†M¸äMD,e‘¨›PÓ‰§Œx 5ªØØÖ&á$Pi«Ç¡¹0„eQ2¹UiÒÇr&ñ`Ç#kDÛW Ö#)¹TNÀ #)ÉhÆ:Ú¬l¥[ o0™† 5!§Fâ‰AŠƒ¤;Yp+ÙÙ¹nñïc™ww´M]]fUtiÜh1Ðf#%M2ïmkK5£>cÆ1š7µvBܨæ®· ÝbË-­QÆV69YZ±¨Ô¼\}µ˜Ã’ ¿?ƒ\qÈÐcQƒí#*±.FA cklÛ È'{B°ddCÔ#%膚Ö#%‘׊Å#Ú‹SÊàÃRÍÓÙßZ³¸£…!ÆÜÁið£p¶AŠâä£#¸TqV0@;‡HÈÌå81`KV“Q¡‘ T[¡sD 2-™A¨…š‘¢˜s‰£«„35I@0Mqåø„ çd×BÍLFZx… ªÐ"Àl¡IH¤5’´‡"£Uëˆ CKÃC|ü|y¢Úzªæ¹S5eÝ¥ëRÙ#)ˆ#%T”% …(è–$œª&±§÷LA½Ýò1¼3"¯”€Î^†Ûxà莆 ±TÐA0T‚"m#%,-srÍG‰(›Ñìaœd›#%=®Ñ&ŒÕ"ìDÜņn,f½´Ìí7LØ0€Ñ¡#%’£X´4Ò¦®6żŒ±¸Öé; G¹=d{`jòTJùžÃ#%¬å.ù¾ÇXÞâXP®®b"UM}ú4ŽÝ#$cfѨŠ© ç(†ÑéƒT¶¨íñ]ŽÚäÖ1@‹uË4¨Ž%EÛäì€1æé­Ä`Ñ°EPH€‰Lèš" ‘¤´Š(ƒä€Ú@¡Ch*±%EQ‘R`ÀF#ˆ—´–ñIÄÁi£ëM@*Iûz{’5JS”6Ò¡Lþ4Åb¼×J¢M»Þ®¿Ùºoݧvܹß]öWêZJ#%µÛ-EŠF,HDJ€Õ+ª}ÃëíÊ€q^‚Ü ž—uÍœÀ**5AÓP Ýœ¸$Kx ºŸOïËÞôTAöaJa¥ÊŠ5ù¤ªÈ¸&áÝ$D!#$I‘ˆÅJ3#$Á.[\ñpù«5#)@ì=UîBsuÓëgi(&B‚)÷NJ’ŽUŽV¨Á PbüD^õ»Y!ô´®HÚN8±lmºººU·ÅqÒ‡wFÁ„í•Eªq¨bP¡›™ƒ`‚{מJ#W×Ý»}ln6§5¯ß+<H^´a¹®ì· Œ6oÊ#)h`¶gÚdÇUúþ-A¡[d=&m¨1‰vw’ÆÝoÆ¡âuÒ޳ݻ‹'t„YÙI€Û㺹›AIY£#Aù„‘ ý-vbŦ/’µ¹NÜð¾q€àÞÛur0O$Pš‡R—ΊŸ®èÌ‘&/7á—“„¹k”DÊ"tÀ»‹™%eí;w‰¢a9}ý;^ÌǶֲxÝ Ü…¯z(#%m6ÍôלåSáÕ¯X¬ø%Ð2׫ò#ôXv¢#%ô~sèub«frIÝÞ/}¬Ê6pŸÚÅ“ù }uó[~ “BGŠhW¢{Žòa#%&xQà`¡Äˆ‰!k»æN‘1rÍö7´ïp;-P¹ÆQ8ÂuµìÑ`)Òib¦½¥T*›˜;kMM ·Xè(#$ý?1`{ð•NÌ‹k#!:…²!Ús;Íbb"ÊQ`‘@U>æCèÉ0öÒ•J3ÛU3·©.\2'è~­/•3Jš·-*  ‰ÄC\ËJš4áÇA(a¢¦#$2¦ø5&º29@q5ÆtÒmú`E€„-“QUýôÀÓ+åMÈA˜ÒPcr#)66i=\£W®m¼Zé­õ«v&¹<ê«Æѹ«•76ºó­¬Qä«!¬j5¼›xÕãY5ãm£gwËr4k&ÛÕp$-0Áª˜@¦†Kp…æƒs”lZÖ#%dWÊ#){‚ên3( ¼=ž ó¡{#F4¡e$B#)‹œ ‡z‚ÕéSûpHv’0wömÝUÇîÈ<#%(8;å(üÑz=JQø(åö¢°ˆ  éþJIcà§J†è €UDÙùûÏ·šäµ»4µÜmºîávm:ª¡;{‹…wÒiuH#)w Ø M©",‚(Š-*¡H!–°¹`$PÈLKõ¿#%Ë}æ›m­ø¦Õ,Å’²B¨?»Àù [1Ð9Òü  ç#$M~ø‡›'Mº#)0µB°0È—Šb 碆&7•*y^Ö”\9Á%/#$S1{1f?iä ‡o‘±+­  èįH=§PÉ‹ÛG‚@'í1‹ "†ØÅ¥’2Ñ“F&•¤ÆÑbÊRRLÉh°Í«EµV5¶6Õ6›fU5™e«Ť¬Ø4ë}%£’ 0¬3 VÈ‚191‚•È‘>Ì)pÛj<¡”„dlŒ#ƒŽ""w ĨÖ`³yD€"­"pȱŽ˜W#)‰bM¡²¶2"ÄI2J‡P.T±A± ”ÌC2%0ˆ) #)Š€‚«Zê0B4_P‘j "@'Íù²µÅÚ[Â¥âÍÛvj]Ó³wuõ?UÅ$LAX¢HhcB¦ ´:ÀáuPÛ\EíÅMÌöâýcŸd‘;*}žÿO¦}U¥Ï-R¨¥{#$ùB#$¢A â•·íÉ”ÈMÅìƒäp‡x#)aLv|,:üo¦& LGŽ‰®ã­ÆEˆ¡z^•ÅX Á€Ð Ùú'|ÃQ¶WÂb>,£D#%<ô¤4 i5„[¦|¡ÖP= é‚ qÞ<òë¥uyeÙSfe ÷{j¹¨Ö±XÕj]Õ¯v¼Ö"^. c˜våK¤úáýDlK±î/FS¦\¼Û‰#$$ÐçÑ~ìaöº&Ҥݥô›é#$ö·ãhþù'ç¹hQn5ËôÚ¦Má+ésƒî銜Äâž› º©ñïåëçgC(Òe"¾Šâ ö賚Ӻo#)ø½J['í¼“Õu$qqÇKž|­Ð ƲíÔžŸ£´Á/Kxâ‰Äí<cn«¹ztDNÝp·OÖ̼’—#%M‡ós¯tå±|­Â8C—n¦åß™_ù9ƒ²;Qv@&ðÍ´­cw%ôxì:´×Ãáåž›¡êÊ]–Ý™å¤ôŠ@:X·2ŽûÛ~„e†j9ôÛ½[ئãßjÌ©ªfékG»óø`0œ8”èïª3ÛXõ̪Ԯa’ºðþJºá† ütÆBöZL4’8œnŽÛ"{ìöÈ»ï¿Gø{æû9âwlÒ6Dá㊠†d"xŸ'¶šém½]Ôw„æ¦{n÷%®†¡ 1ååRn#)Ÿ¦B_Šsƒ#%¾î┪ÙçÏèÌm-ü¦Z ¡¡‰ú{ø¾¿EŒt^,Q”HX¡TóZŠ•#%äAÖ(:Å’û©Nˆ‰ZîñŸèŸI¤¹Gׇ%w<´É?}‰ÒÅÂâa@µZ–¤{®¿Ô;ai­Ã´zIzö±Ì¨ë¥^_¬Œ¤¼3¸Ú"šZb¨ŠÝRÜiU³ö¹_ê}9nwÐi÷Úýþ= lªqÑh˜êCâz©9ªÈK8ñ i¶Ù{,UbFá3ËÚ¿#)1˜v-¯´pT¯!Î~×™ç8[¤"Ó=’ï~tÀá#%qQ½s%1Г¬>ÖpÁ¬Ú‡àÐÊa¨ÙvßÖ#%åÍg_* ø~b;M­®™œ 虘¶â­÷kÃÓ[é“áã§f´ÈË€çVè‡è·EA"dôð’I'wEœæy­Êª3o:,OíE‡@‹´o¦X—cÄrmLÅ1:c-ÛmÍé8â^Vm˜Ö)Þ¦Ä0`VŽ#Œ–áå5³…iÇüÄmŽÞ$rïbsLÚ§ÎñCh¼b9´§ŽÇ;o­n‡Ûû×]ÎОeä’yxÌÔoQ&ÚxS]±QãX‰éuðì8¤9dÍB8‚µ³Uîíä+4ñ{꧚öª¼æ{Æ‹ü¨äÑî0‰Çµn[›.ÜKc~H¿_'ÂÅ-{¼°^9ÌŒpäuzó|‡;þi{+*S¹ç/ÍÚG~Iñy§Íóóš³{É=/Þ’°p^WÛÅ—•zàœvx9ÜÆûW:ñ×c“Ó,ûfkOD»¿j<Þ‘(Ú#J#m»R­³žS‹ )u±lu;t#%_8Ùü*aª‡}¿­˜ÝJ<z&ƒ/„Áí7Ò¾÷”­?—WPÄïcé7^“ïéÛÓÌÉ߸uíÐ1¡4dEèÏ7Ç”qÙcPƒ¶å¬mÖœƒVp¡Ónåkw1O“ÒÞBm‚ÔRôœåÎ¥ ˜ß¡Mè,¦îlänÃÏy5SC#ŠPÐÞ´ø ×mÓ$a[̲Ržkô1½Ì-2ÆÕ'À‘‚9'Ñ—œ>Z‚š@#%´TŽ¨_1Åéª* #)J®œKÂÄÜfŽ™ìuLòW^!ŸÍw"㧖 H7…é è·òÄ™*†„&HmÆZëªê$æ”)¡ap×Q6“3‰•‡…Ú ÊËeØä\0È.#)tb¦qÉ##)Q¿¯×¨‰…Ô bÇ-H2€ 8á¹:>“öáëÄ-–4K³hò’Yr ™ŽA ­ù¦`ÇŠØ/lÄ­„†Ý$ Ü5R(ȺN¦ÁcxKÕANHD¸XD„Q0@H-†øhŽFMºxW^Þ¼qË;%·3™BԳá #)Î(çÀÃ-ÒIÏ!®£A25ÁÃ3Š€Æ‡ÕN;_/)¶#$9ÀùÄqA%XlÄÁÉá#%xQGÛUk挂k”P¨cX‰Hú&š(‡Q7è¯#)ˆ]4% I*PQõ n˜ªdæHĹ?!Q.×ïˆBí®0¶#)lq™S¾£Répû¡ÛÄúÍF6ž'ª!mÕú#*=Îm…Ì¢DGh´®ž!-É–ö’Åßnúª0â[?E´÷ƒà»£uk]ºcb˜ƒ#%±7¿`'¦Â0 AãjÙú›üôu[ÚW±9LÎGÑʾ:ÈJ‡¬!˜€²ˆ÷;­¶}ë 8ãÆòÑpŒŽe¸©ªÞBuNõÒ²TB<FPúèpKRÆmàJ01D‘šµ,yñ)l#)ËOãb¢q]ðø3<ïı(®áÙô¯VðÑ›Ûï.fÜÂN™¢¸­b0ÊV3õ¹iN® ÀùŒ‘ÔçMɾÝ6+³n9aéÇõ¾b¸ß¢ß¾%#%Ù²Lºã–鄺£6Í¥“X.ŽCkâ^Õí¾Aþ¼Þ=ãòç•ön¥˜-ß½¹å/ºí}ãW0ÁækŠò—KxÊ–¢aF<wŒÉ“²[y(ê§ÍÃsq yHï·Œ¶Ÿ=ÁÂx¦o¸fù‰/CwÆÎ3Ê™[V¨W<̦É{x09b%‰K/kKV%¦CîÆûi=TN*PUÒ)<Ñ,ûqÁS.•Ì¢É)/…@eÜ á8*jrbñ&°Tù‘æc!¢õ—tyÑ“Áß¹“ÍÎOÀí™-üY п6 Q‡l\a0…ëN6Ëð‰¨jœö£°NYqpÕ¢ð.XHSE‹8-²8‹ \½³Öëdyfgœ Ê9;ZØXm@ljae‚1™áCÞÕ²©«¨T45à£È¶Ÿ^3˜{©V“PØò•Ü5M(P˜3iA¡°€T‚;Ð8Ik#´âÆMûäÖï0ât#%GT9‚Hð˜mU’&÷ýÀ×]õûC–6¨a²l#%~ŸŸ(û}Ä-ðjÖ±úìÔ¼Þ‹ºÜ1ÓáÙ1ð;l›åøƒÊŽY’ ¿Á¯1.ªx<KÔntùw\—Ë<?‘‡œ<¿Ãû—W‡4;ýaë^ÂØÈö¥£HQž^’øCxCìC`ð8»ÍÁÇæ|ëkŠ)R,ó¹li݈ÕûY¿h¯m«»ÄÁY¼æ ìЭ#™¥KF™@¯#Á¥|kÅ×&¹8u]Û;­Þõy½¼m½m°lkåÉw[–‰/(¡–bH…›ÀP´¢¯|P*CRÛ[Ũ¶åª6Úæµ"#%±³¶ ÓEI96Œa0 ¨2*"#)¢Œ…JrAîñ¾rñ¯ ã¿b¾Û#)(PC€èôgwcTvºÕýÎ3Ì,˜Í÷o¤D¿bq^¯xÛ‘pƒ)T<¢$‚‰  U#$2 2˜JŽhnßsT˽>Z¹Vð}ðÍ}·XñËe£ ¥¿Ëâ*Æ1ƒF(©QL¡ÎùáF–†àA¦í¥ VS‡þ?Ó0lál‘^#)1¦™@ûæ•\“v#/SN0Á*Ì<òº¶Ô#)RÎ|óM±A¤+ ÊB4È¢Äpùq§©àË#%V®ÈˆÄìÊò”Ä5·€òJã0‚j4›M¥Z’)a.UXؘÆ4C)[ÌPåf²Fl²‹XT±‡tÊ]$…Y/O¬¶áí6 ŽBîMÕˆ`&j4àÄYeݸÅ5en°ÔMÀ(Çc^Ç{8cZhëŽv²ÍîFjh£MÔU¬zkZÖæSÇ‘ÂåËÉÆ.3VÊß ˜ƒDE¨ÝŠiU €å ªÅO.QÖ\16Ô³‚"¶&s-~›ÞµÁoDŒbE6ÈvÎS¼ÊΘ´Ø&0‚9Ü6(JR#M#˜t³LÍa¦uŒˆQ¬aNÖ•§¢C›#%,ªoW2¾¥dÉJÌ$»ÖÇŒŽ8Lº”UÁƒcrz¤¼}âµÍ#)D‚nKÝÛWm¶ñwfÐ#PŒ{Se"r<”+C׉ÃVñCDÒ41œ¡’±ƒjÚ7 ¥PT¡V·š#%&#%¦#%ŒM0¾5hÛÖâÛÉ’Ë+·®v,ÚØM˧Ƃ „Øh2‹…³wdeànkh0ÕŠócºlÐB²d)3É“.ßë9/ ZËß`xïh\(4Œ)±å°~Fœ‘ùÉ{*7ÚW¾î‘05½m±³¼Ã¦cØRDØÛÞX5X20q·ºn …°¥#)Óa]`wF:ьд´yÆpa;ë+`æ¥5J©XùyN{b¢ã™Ã&FÔ¡áYUVÒ5‚‚§ûl(ñj>31öeIÑðV"Æ…¤Zñ¢×ÙM#)/ i Çn  çaÌs)Àñ¤™ÍmÖ‡Ã[zX[2”ÀÓ&¤0ËÇ’m£EÀ}ØV®n`L3ÉÜ$g>»YŒŽ§â4ñ¥¶Å³<e`æecUyLjTËlÉbFÐÐĈÅ=Ó9HÒ TŽ,„ Î ß}f&º³ Þ °g~ 8¾d΋Yã/z»4vø¯[ØãîõËŸ)TÔF‚‘)GaeÝÆ M¸ñ1Ÿ0áîÿ1™^h0XB4O!}+J‡¡¤¨!$/‘¤(w"õNùP€}Eàœ‹Ñ¶Ïd£^É^Œ2(ÓUÚA܉Q‘ÁWžßB…ö°¯‘¦Ü'éqº²±eÓ®38™~§WÔð~ð’@ßõz5ô @N-²I›ü|ý?FÃëgJÈ ì~¯/^zx>zH10öœœÙ¸›ËŒp~¯/ÜKütuŠ#%–ªW—)™óñ›ÜõÛá"4ÝÒww&#¥ÇvèÈʪsÈ3<Ø‹g“ÝÆuêNÐ÷">ð:ž1pa8¡I¢«7¶Šjä~¬B¤ïéêMMÏ8;ú>Êš–Š€'qU_~¦¥z71µ²©”ÕhXªˆ’…´EK-H/@ïýSðQæD6ιíî«Îi$ BB6©QSIm¬(,†#%ASI#%TÊÙ¦McY5«J6Ä›IŒ1›*,i¦Í4¨Y%¥’¢(bšƒQ)Fš)š6M†•L- k 6aB‘@d$EOÚ_¨ó•å0TNX³‰Õ´LEõÄ·oDìg-ÇÛ7  ›.f'·p ò.†¿« ¿ÀC#$M7‰Óèí¶ÎFïMŠIð—VÐO0Ì(,‡…èY$C*‰î –ÊAça@mI¤wö›ð†Œ]û8ç#%0ÒMM«sàã:0þ¬xÂÆI#$Q’#µöm‰o7•ÐsÛԥȨ˜’Ä.!ºòI!uFšÞþcÝçñÌ}Úó7‡>ÈÀ½U âY®¢FøèÕŠZìs S¨`MçôÕ~µ~£m£%lšÄE¡”F©5¢Àµk›H¶Rj1X3ñÝ«™¶Ê¥å@)T¥,…”sà Û†•ˆHÂ¥ !GßìÌ.#%eYl}Ö^ :{ À Ï›h…Dv‹Xiâ0Üü™tj±„>—gCµEh¦¤#”˜ˆ¡¶ðrA¤E ºr’³[0 C,[ ±w$Ã!l$X¾C2ØÁTF£CçÎ×™F2”íïšù•îM…¦`1¬iG~¸³5 £31„JÀïy”¨XÄÄBŽ ™‚#훞4qÓM'øÐj%3äá ÉŠC’ ¹rÌiA$yàˆ`Ìëæ!r6ÞÞ¶» ™‰\µÄ›ÞÚú_¾'÷Ó•`=Ÿ®lG:óxÛÍÈÅ*‘ÑÆE‡}&™ XCµÌ¢L“¸ @ç0H ¡#%ÌÕRÏ[.¨T”KšB€Ä™ @ГBý¡‚ª³ † êßѺL#$Ãgšb¥*‘$@ èël'“öái¢¶oÙë›Ïwo>œgêJ‰å(lÏ Ðõ‘! ýØ_ÔýX“¤ª°lyy•ƒ.j!ï:$ªŒ¶\°\j¦Û À}ŠÂRЗ0A†}µ#$»¡ÛÜp÷ªüfýú•‘¶ #%ÀÛ:-«Ó·ŸÛ5j»j2ZúÍkï÷ü3M±±ßwƒD³žÁûÏ…Œšü#)ƒ­ùF“© iaáñm1Ø-ƒ¸Ë5üL?mJ?² $>%JL! Òjw‚g7E$,Q“bîŠÛ%Ö¼åiÈ1ÒЋêôž»‘:|C‡¦.Ä6WÐœ“•®J4óêFEªèÀÆm{I ˆ2/yÏRî;¸ºÜ»$Ìô|¤Ü»b&pù̶ù)s@ïJHRr=F½Iþ‰›ó0l8Ü`ð,ív»Ëêâ„“6z9ˆ¥ŒLvÕ‰²ì Ø%Î$$(’¾ÿStÒfê¡‘00}»~„n„†Œ=;O·èw°ˆEAI"" ª—Ÿ¬z¶©Gq1/±ûu»cETÄð¡ÛÉÂHr ™ö¯¬VÌ~WxÍ“ó«¥tN;ÕK!UÓE†1‹1ãvTÎïÉ z!ºV˜<L˜ŽEa§´õ†ôíª‡inÀ;½‚ ™_/­ÛìÃx`¡ŠÒð†tÊðÖç¾°çkxÛš9/Ælèã:ó$g,!`Øé©=f)1é¼ÎŽR¡$i©ü]ñ{c…H5áä\]ÂÃ>,àD¥"O3OÃÆ3YÎú›í”#%[0Ñ#$po4DhÀ¤FÀ;â3"`âPHšŸÊnªÄ°dþ!QPÓSœ‰0|ðÎTS]æôofø#%d3œ¤Ä`$™PƒIÌV\€!8Ã%Œ7iŠí(£Ja-4ØγB#$caé 8 œ@6MSg~5,3g«ac\d¡ƒ.— 5ß»y¡‚ð¹–uÇÌ°·#%“zéery06CÅ°N,%D‘#%h¨Œr¦§™ü  dbišcBã‚6 Ñ‘vEyH÷‹#%àh͉*·ÛF'\kÛÄMª„À A$‡ ƒ ,N‰ÝÜ`ãŠR#")7BuÏ}zèãp;몕¡ˆM“', $jÚf©Ø„$3j °/P`Ê5Qçåþ¦qÆë`}ºYÉ¿ŒŠ‡v‚L!Sœ¢Y ©ºãhj„SºÜ‡"(3Š•æ¶Å~ù„ákŽd3Ç7‰®—†à]`™[‚KÏ}mÙQšÃä¯M»‡NJ`–t[½C‚í:J(7f²”ºÆyª/¹˜S#nìżÌ3Θ”J¥¯4½i Z €c#$ ½-`•‹hl@šh8ï#$ZbÙ×5s#){ÑRã¤#Ê*I·‡™òŠR®,,gÅÉ57wyÏ›¬ÁŒ«ËH<fUß„¢GšÁi‘½ÔË® —fZÄ3åãû"‹–^9Óᇉ$YXß*+ ¢æS4 „×i#%H#$ó!œäg¬$eìÊ3 ìЫåh0(‚k^ ¬â#«ÊE:§"§u×n؃H¤Àì'~tö\bIIeñ2dú:ÇM¬ØÖ–DùG$»h·b¸EC`LBzáƃ-#§#ìƒ:²¤ÛnE-–Eî9€€Ý%Âj„åH«j.·Ä(Ö¦EED&µ+b%7ôjH¶BE÷wʼnžŽ3ŽÛ‘;T_W…+ý¢©mz¦Q u$À“RK Ô#%p%&™2;z%³1a.¼‰X>uYÃÄxW5­¨´MxƒwÙ…›jnªs¼7 ,SìÕ$k¼1úÎi¡ð–-uy繜gXf€‰–ç®u I ÉR£‹ }©¯§›»\Š~EªÃ“,ÏRæíLL1ÙK9ë×G®N#%tšLšŠÄS(ÔnÌHÛ&l‡í¹Žx5-ò陉Râæu³‚À8âÆUÓ7K¬áåß‘ :ì2k½¸)òJPàpêûè,Á™’Ôåš›“HaÒ ƒ®†a‚Šca²ÓM‰#¦©b,(B&’cC¼)ÃSŸº--Î(2Á^ѺÓéi"è®’ 9s¾#);ʲUö’Ã<] sH\­Ê9ÀcÚâ1®®È’“‘Û¿à·Û,iÍ>ã5¿K¢“P½G#$¦*€j®5ožÅ±äÙ %ÓïÂì-ñ†!DºZ:ÙÁntQÓ6‘Â$üoöùJ!í²æ\6E.@ZòXšh?¯'+o;o­0Œ€„#$ÀsdìM^ðk¿>%tÍ« nž8ùùÖ¸cDsM|y’®/O‡!¨ï¦QR§Ðai¬½žm£y¿!`L°º²RkPC6 JbÍ;ËIñÓq!-oŒÁ¬,¥C´:lSCòÌ/ÓÎèÀ娘ˆÐ@m‹˜*ì!ÀÌP•:OGë´´¾±˜ñ‡(It ºpw¹—ÌïÒ]»;uV­á#¤ïȘ}^Üq“#$…³Z«&7€pŒ˜Y#)Œ%JahüŒègÐjòç"¾]•ÒÍéSfÍÑÍ"­¯«Y€kyLôgÈ‹ÎIdx¥:Îé𖦥59ç#†b¾%ÔM#%ïÁ@áƒ='<ᆎ:¢Æ†0ž9¼1âgÓ²cM÷s1Äo¾PÍÁk±4 $´òˆöbîó\U¿ Ä,Žt˜öÃ"Ù¡n${ ÎŽÄHÅ S%…p@¸fƒ8¦òCi¡›üc„6ä„E"GņdŽ o¨a¤‚+Š¥#%“Y5;¤ÛÝyàët÷²Ñ9%1«͘(Á7a†Ù¥»(dÀ`¹˜Ý‹DÌ" #µƒ)Ê3+‚Y,#%#%K‘° ¶‘q\] STŠŠÆ&ê`d”^Ë%3¸h³Ëã|0…Õ/e—o™Ô«ÊÓ1¥±„lˆbßV•\u9º£(в Ú¢ ÙÕ‡È͉£”Dž¤H14Þˆ#HaØßÍFFHH¨§#@ÈÀ#$Èv%‚\¸ê˜˜šÖŠ¨ÕѨ›3€Þà¡P›•#)Œ#%Ìkb‘À#)eç+„3œq»§XÉHg8i¡8ÂB¢ê™*L"ZBÃÀ8¢ ²}IåçÒ|L{(¨4´TDP;qšîEÑ6ååÓ'¤laíä@‡Xhˆ°‰ 5#$¨Aû÷ÿ„·–ËØ>G¡ãìz›Ü=Ë6Rj%×ï+LÔßi©Júg/Ð-#%D±¼’9tæEî /ÄÆôÏe1mÈîºVáÃa7K¼f$ÐÇHÚÝa€Cj —¹$eÈbŽ&ß™,W˳šë–µyUAéµ º29K–Šë¡A7ÐgꫧLJ”QL‚lªG„Màö3]¬ÑðÛ½4ÉÀx4‹Š”µ#´¹DMøÜ™b†‚dçVŠ­Ô>û,’û ­d}åh/¤‰¦Lžî¹wl~ \ÐóìøÏ&~Ö´®¤aÀCŽVŽsJ.€ZõƒˆòX›/®ª­ ¯fãs½5‡Öõ©Õîó7PÞ­ÝÐpÆ#$H_z_|þö¾à#$Hˆúèljï>Jt0Ý"c\ŽWÓd{öÅT°“ëŠm²›Œ\z~x¯ Š¸Bg/­;µÔ^W¥'n‰Ý.Ûi‡oyá* ㉼ƕNÒYƒRÁ½ò9wmy¤„’Q¯$Ôï$ªDØÀay`˜Øì! d…(…K–@„@¨¶XÕš1Q¡Á Q™÷Rd+#$ñN×ë$ί=Üs}õ±Ì&²š24°L0ÑÈ.RIðœ 0ß<ÝG_cëíð ì˜÷ÚkµCæQ:¢ À8„Æ'ta“kÊKUdFwV‹S¤‰JF*Bw332ÌAQXDƒ@å0¯> õ†ÐÊ )ú†Ž#)§¿ÔJ#%å çæ`.‚›ØÃWïk9õ}–sì4£ª£JzÑ›*¼ó!d?qAaŸ/y4(ˆPø“„Úð¨ÆmëNzkÒDårFNþë/X/ËF£UɸÀ¤Ë–q™oµ5?TÌ›rŒ›¹aâ ¦0†° §¶=#)‰~nx/G«p¹&§#)3pfédø]¬ ÙÆ%7r½€?ıQˆ¬Dô ¤ø›Mm›d5ÐÆŒ 1­‚‘è_”¦DõÅ*"{` E%²ÕiFj¢Köed¶£jMQ­o‰­“m^ûn @¹Ò¢€ÞØYV¢H)dˆ7HŽ P"@X M€¯d@v€"ûokqÜD…¯÷h#$°#$c&þì?L›ð5øþ¯o»}­bÒÍ&ŒcXщ¥ïßÝÞLÕXêg 2#$ýÁ‚€Hˆ*$Š¯Ç#%»}¥÷÷Qã`¬óë7!݆=>òt“ÜxÃÓ߸[1F1œ‹à1§˜ 4a¢L"CM£2XVK’ƒaD"ŒTv¢DÈÛƒœƒ#$Š´ˆÐÚPrP¢BfRa ±#)‚í£ $â'Q¿K£Ž3#$4*:éŠ#)»€,-(l¹‰f‘²½}j ØzcØöŽ/4°À„Â|×'Oà}Î,5¶XÐÔæCNï…£gʦ%ccU ºÃ¯¥°v€l )rÄ‚Áˆ#$¡±×‹#%ÄEàb 7ü“´D“jÉRw–¡@$&T• Œa„¢ïih/|žœ:C ™g†¹Ee#$Pf¤¼r!Ex{2›è$Çml”ëÔöoÆÅ!#%ø™>ÄÍÅ÷è²mç·#ž¦ßw!‘+Ž]¶­6Ó¯z†®&â¤;%¼6WËvrr?&\Êᶈl‡ÔS«¾:eOQôzÁöE#%†±A=…†*A ÓMZ•A†[á#%u%ïé!rKïl|²êO¤ò;Wݹ#È çÂÇ¥e­ÊË5$©¦¢cAm[0ØJ„Q³ZQ­R[÷”V®3•Ò@„R‡s¼ßlñj_Ùˆvè®ÔÜwtM½ó7@<îˆqðÜùˆ.K­Ý³Ž#%`!¬E&|ˆÙ=‰Ýà:õSwPˆu¢w©ì}·#$ä,Cƒ³£RIƒŸÓáÓ²('QïÀ}ulÿ  =$4KŽHÇ×,ŸÓÕ‡|ü☣qÇH¤A¸ó½¶’a©»¤îâƒ$ã,%»É`ùÑe®,§DL\³–ø…héñzI¡Ô\#hÚi–Røj'»uM°t‰¨gCi¤iKb Hm‚bÇkÍ<=\”’e>³§|%Ã;í[T†—°€(,´±%Ô bÊ„Ì̲ÑùµvùDMçrœ"1ø} Ì‹fȃä8Vqª:6bßæSXˆ.^Íínµ‡'üKêŸÊÍá$‹¦GNËôÜ´&$C«¯ÎóÉa¸®õݽ@P)eZ·ÉA.¨ú#$B§.ο”ãa.}FÄÈ4“«#%1艂dA¼O0@@#$UR";^¨ôñÁ7ûÏmï»àP#$Wq×âw†(#b wIT›N‘J0½aEF†½g°“£Å‚BBq;L°×}ÏžÛü½,Vì£.ÌgÚ3¤t(p›«‘²QHV„Ö½îa÷l#%Ýîùiäµd#)ÚˆXy½C^Á¯ˆ§`«7eŸÐ‹ÒMtéô÷6ê¸ví¢åU¨v‰Hczì—n^6÷ÙCGK¹Žkä[í*¡à#%Æ…>16‘rbJ;ÕþØM‚í†'_SVkËïQF™1™<W*+$»µØÉe}ÝæA¶+÷g=òóËÞãiÃT¢›!hÅ•äÝd#)ò6Lj6¢°†1–êM©–Öí¿™JFÄü ØD6ŸîËvߟÜxxÊ((õî‰â`|,ÏPxVšALGÎkó_Hõ*tõ‡_XH²+"1F2 Q‰'¸èO0 hxÅ £ €ŒDeŠ™Z,–Y’Ù³jüÿ³ðj¿‹¾ñ¤Q¢ÙQ¶4›JSMªü»oåîò„Ëæ½cè"¨TDÏë„¥­¾Á™—BSÌA,HsHNt"N£ò`¤ÔL#))TS")±Š‰µ–ÑE¨£ilÌ[4iïžyª“V–›RŠ']ˆ„]àä€a~P×þOG-@õ''Γ€$íDM ‚sÔQ¨#}*ið‰SK²ײá 1>A‹{by"À‡`i€p@Êr2Êaˆ’éÅ=ØJ*½Çw‹!I ªNm'ШW®>Y˜’ú)IFw B$„²~7'-=6#)Šî€Z³#%^`:£ód£ê²&"‡4öP A}Š@*Rf6›#)XZ“k›¤µSmF”š¥´ •A‹fÁRæ­¶m&#$ª¦ÎA]”+óê`àJ”ÏH€H°õRR÷jc×Øméªêù$Ruc»Q”ó—0¹{=9¥†ÚcÐòø¨[:gÒ‘°ü”+%3OŠK⤌‰C¬­˜¶Ò”qÎ.9˜ ~ü;»šãùêCf¤<ö6¡ö›écÚ>åóþðÊ#$”d4g…HÓ’#FLJ’4FXÑE%Dkc&ªÚ7ï+~E¯*mA´•¡Sò~RJ T U#)#ð‚ŒB~`é“v @ª‘ A‰‹#)<¨¦‰Ÿ1­ˆX)vêç{çÑë·d´×sˆõ"ÛUæ´[QŠÐZ”* µ6X‹hÕ©¥¬©~©µòôæmògnöÒ«,”4åëÇíTûP¦S?Ë3…ö1š‚ÔA#$e ÆoÎœ’Mþʘdú§!» Ei).賋kŒ¶!´F8•$ TgyŸÝƒ°¢‰&ðBÒÈ]@¡–Ñ>‰+Mƒi¢•5îl5ÊZ7mwvWy¼­uM¢É­ê®J‹y»¬ÍÙ™U×7mnuC5‘e«Ë»¢mwuwv´›*L˜ØÁ¤ÚCEÂD•8)V@¥DŒTm¶Q¤#%6âÄ[Jilš“,«•®­åÕמv¶ñ¨¶Q™•e²–¼›uwn²Æj˦ºMÍN¯‹œ4<Òb2B¶Ä˜ŠÄEË‚ý#Ÿ &¡Ì’pÐäM–%ü íý¦‚´›L¾ug´©­]ù>o5û|g3 tR¡úa1T,½ø˜1©›$™:¡@ÀœÉÇ­[Lànw¡™Ý®K†b.¿ã8eûp#$0}0’ì$œˆöwêž|¶;ÑXE!]7»“y<´œXÛ8ÓÃ;ñÑí^îã~07"ß_ úAi‘‰1êDÐÕ)cS{é[§#)š`SS9¹„c }^FfÌ…:³ë·œA¢Ž+”R3‘x\ëÔ˜Þ9„þkÈÁ%Áæ1‰¢Zmaãéç/ZÙmÝù줗õ‘Ñ™o'ý"Ö±ÐéõIZGJƒ×[ë õCHbyq• -…-Q”4ÅHÇ¿å&{$Õàòí¡|šAïŠÊÒ#Zi^ªŸë7#%R„¾ÍûN¡¾;Ž¢,é‘|þë{öLͤ$~zÄE²=z’IhÉ‹’…#YÂÂ2“p錦-*P`šl.:3m30À5q=iÔ& ›£°5Ñ—@(Bû“'XYŸ7käN% ÝCd¸²&)vCÞ`‘l±(´ãWVW¤Ý=à?ËÃó@°7­Õ ñDĸpZ¦ðËò’ 6Q~ˆ§„ ÛsCãÁ–ȃ·º’ËŽxFêßë¡jIb8j«Ûäe‘R–é^Ó³2#%ƒúdf•,?Š*Ûr½Dæv+åV¶ómA¸(¡“ü4,ŵ9•Ò‚k̃#t[Z…]»Ó‡G~ªÜØáÂ\#)r£}÷C“{QÑ„îr§½{í´X q8p–hæx%þÄÌç†b– ¯-.9¨.Ã,¸ó£Ìa‡rM¶ Ó‹~BœN“xó$ÉôKšžg·é± ‡I¨ñ>iÄ*ü/³Ùl»Ì´4õ]1~Ü „O¿/~+pAÎyR‚¸@±HnG«N9hBÆÚø¤v‡„òG¡è…ÏĺïݶÙ>U­Lä¸+“ 粫hÿËÿûûêÿ/ýýÿù7ÿ?ú~œ?áÿG£þþðÿ‡ýëÿ“Uïÿýÿgú§ðÿ¿þߟþ?ò×þÿû¿Óðúú?özôø¯û´ÿúû¿ã§þï÷ÿŸòÿ…ñÿ‡ðÃÿçú|Ÿé»þ?/ý¿éóôÿëåÿOÛöqÔ¨‚߽Ʊhü@€u@íl€èº•pÿ)ýÐ@ÞM„Áý>ö T#)¢&B€sUs0)*ÿäŸß¥S»±$&f}ýo®Ùm~·tJ#–{rôk¼ »´Ú!®<ÎHTÕ5„ØàEÿ轤ëïß¿je´Ì.l‡ö‰noÝn*j˜=ÑÖµFIÊ:ì+¾H1 ‡0Z¾Wf±‹:× @¡C×ýªÏ#%o™ä§üþ–Lõ3øKþn6^ƒb9u(ðÔ˜ÌiÇú¤Þ* L„‚ÓI }}Xך«Ã”ä’Þøïíü5ÝòÎ$|e­1Ìv±•‘ž#)Ù™þÇ``ÒUÆUTÚ”Ï#%&QFišÈU4Ó.I\Ã#%ŸúphÔ!2ªx´o[R¯­4‰—€ì`G… “³ºÖîN&ËŠ¤ÉÊX!l– ÷4óCMÚŠ;V©³n¬fõ6ÕÆ´8˜Ì‹Xqvak#%í] I˜qx“ý2Í…’îHÁ#a#%pÛÖ=_b°-9¼R6wryÑ$´¡Ý#utFò÷D3ÞiÔ=Ðg=3YGýÍ„e/€ýß 6P®.f,‚Z"fVHÄb×/;bò#$c.Ð6‡í|mÄÎTcæQZ§ðHuo ‘ØáÒ2jǵ.cb¶*t\¾Üº·ýjÄzÿÕ@ÐeAeÉãÏgÍ 5æÞj¦+¹%Y‚sEŠD|i'‰aêõá.áI9C\¯0ÛÍïÜømà kÁ‚ª‹IRŠ]üºoè?hz˜ñÐ,g35V#) yâˆ(RcŸ»n)Ù·S.‰¬$d]®épÅC(ŽÅ€#)ÈÈ ;Α¢üìØÂH b¡$cÖ2âï(wä¸Ëb[ØAöýsÄÌá‹ý’uÊÉ€Ÿ8Ü—ÊÎãb‹&#$œµ¥#~F¥»9Ö‹Î0•ssIVÒ‰#PÞ.%D¡¨*‹VR™[bÔ–¥4ɉ5öݵvÔ¹eH'D@Àˆ'ng¸ö‘ âë"î‹1áTÝo0PE-‹'CØPq)¤¬Qg?ˆ|C˧”±þùgù­ãUh„ßc÷CCþÍêc“Y¯=˜ŒPø W>Ï8Ï/û|6iš·ÆZÈo´ÉÛRÆpÿö_ÙCm¿¤f{عe|1È·Ô{:«SfŠï­Io‘-u#$8cÒ"ñMòiéOuƒö~/·Ò&‡“#9Éf†P4Ò©£Ë3|½’l{ŽÂ}4Xj!>z˵Íãt¬m–è¼e¯«Mëò6[A”¥4­Ùap†„±o£[ìnÍ~½Z@­Ísi6£mËSJŒo·ª6øV·¹ڤіZ mó•\Ä™d,Öì ÍZ#)^ðJcvPääR$8ªbcþ*S÷—ÝÂá$$Fàn1t#)"u:»l\í6)… bSß·i%ôì™BkU¬ŒŒôRѸ(;ˆžû§Õn¸‹˜œ(4’,€HÍãà)_ö^Ãçî£Í“ÐŽÔs}\EðâQ‚ú˜póÃUþ¨‚À ¡µÚŽã³@h’”* @W#)ݶÝe«îh›5&½¾ÍV<¡ H$€ #Šöú½žìœÿ_@‰Ø„xtù@òþw®c#Ûmh‡bÝÑ::1O(4AŸ“Ãêûƒðÿx…Oùõû í&Æw>(YÿÃÄ”"×ýÓn–ŸÇþ©ÿnþ‚©ç10…?û5ÿõ´¯M»Íy…]¡,Yû;ŸÓÿËû6^U÷xzþ»6ì’|×í#‰Í>xð{Ç]Üó#¿ÁÃÚÈ@{òÂÏ?†ñ´‘ì×þº‡Ÿ¿€ÑÿP‰ÙóªyˆqLbøá¥áñ—ŠåÊm‹#ñìÅ×þÕf2ô#G3عBºi³GÿŸPŽ¿2¨•˜ŸéœÖ2’ïÙGí©Gþz-g¥vñ8ª"™Áתáq–s¦ò8‡ÇÙ´éÛD è2²±û¹ËxFá‹Ëa,oÃÂ4N*%¯+iÌ$]ÏNyfαb‰Çz«ø½n»2X¥ë&¸ ²¼>ßefcÓžÆÞGi={í>Ÿø«#À§œgPÛî1ë–«ÂO@uk§øõÿÄc±üÐdcù?_̈ÿñw$S… 㥧Ð
-#<==
-#-----BEGIN PGP SIGNATURE-----\n\niQIcBAABCgAGBQJXucfbAAoJEEm0xnwFJ3qq9HsP/jfAbxzLd6uHMfpZP+GgGJeY\nDx/t+p8lKI3r7VDXLB4UkzljBisNG7/uNhHZ7FB86JVP9WTKOjkRk0a5GyjBHI2K\nNFBxC/cUljiRs+2NA72MPFUkGJWG7LFeYihWhpto5iNDGtyBGxYOn9+wlStixOIP\nVVbRkTxc2Pda+Rwqj3zfPVZHXWxv1kPWhrLth0Y/20Q8CBVa9QyDK6VoXgBY9Kr7\nOhijnsGAX2R+CVfR4iCM5/QpmJClWb6+ER1bPrvyCdTsCzOZZLzvw3+U16YYzrGK\nWj+PE+Lfbj7GizItZSB861BHcl9yGWd0p7MxYoN8tKZljTKksF53r/QXm2JVkUCZ\nl6vY42scP3tThgyBU4OImokEfw5GY1JbaEtzi2sB7bdzcMVgTCWrzozJYqEXXGlm\nPY9Xsqr8p0YZT6T3l4vFAAxoNZ7vBcyxixiOzkygX789/vHurylRBLsH8uSlMt9O\nqRPEV+c/NzzfrxKPlfjvX7Jzzh6Fdd8QzLheKE4skSwbQ9Tb503wral+tPSbKvGp\ncU9PWFlB7W+TiPua+XPg4wkA06lwo0yhDH5wgLme+LkeUaQjHo6+MCweYjZtSPai\n/fAre5LUhHrUpWPogvWSSdq6eK3m76utSSkHCJAj8mvhTWV94A21Rv6dutRvujKo\nCQGdw9yT22XvPzQZ74b4\n=lYLz\n-----END PGP SIGNATURE-----\n
diff --git a/linux/waf-1.9.5 b/linux/waf-1.9.5
new file mode 100755
index 00000000..514ac755
--- /dev/null
+++ b/linux/waf-1.9.5
@@ -0,0 +1,169 @@
+#!/usr/bin/env python
+# encoding: ISO8859-1
+# Thomas Nagy, 2005-2016
+#
+"""
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import os, sys, inspect
+
+VERSION="1.9.5"
+REVISION="0c9b76c74624eda0738007bf6caac806"
+GIT="57ee30760a44c474c292bb67977b9a5d62bae58d"
+INSTALL=''
+C1='#0'
+C2='#/'
+C3='#,'
+cwd = os.getcwd()
+join = os.path.join
+
+
+WAF='waf'
+def b(x):
+ return x
+if sys.hexversion>0x300000f:
+ WAF='waf3'
+ def b(x):
+ return x.encode()
+
+def err(m):
+ print(('\033[91mError: %s\033[0m' % m))
+ sys.exit(1)
+
+def unpack_wafdir(dir, src):
+ f = open(src,'rb')
+ c = 'corrupt archive (%d)'
+ while 1:
+ line = f.readline()
+ if not line: err('run waf-light from a folder containing waflib')
+ if line == b('#==>\n'):
+ txt = f.readline()
+ if not txt: err(c % 1)
+ if f.readline() != b('#<==\n'): err(c % 2)
+ break
+ if not txt: err(c % 3)
+ txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
+
+ import shutil, tarfile
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ try:
+ for x in ('Tools', 'extras'):
+ os.makedirs(join(dir, 'waflib', x))
+ except OSError:
+ err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
+
+ os.chdir(dir)
+ tmp = 't.bz2'
+ t = open(tmp,'wb')
+ try: t.write(txt)
+ finally: t.close()
+
+ try:
+ t = tarfile.open(tmp)
+ except:
+ try:
+ os.system('bunzip2 t.bz2')
+ t = tarfile.open('t')
+ tmp = 't'
+ except:
+ os.chdir(cwd)
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ err("Waf cannot be unpacked, check that bzip2 support is present")
+
+ try:
+ for x in t: t.extract(x)
+ finally:
+ t.close()
+
+ for x in ('Tools', 'extras'):
+ os.chmod(join('waflib',x), 493)
+
+ if sys.hexversion<0x300000f:
+ sys.path = [join(dir, 'waflib')] + sys.path
+ import fixpy2
+ fixpy2.fixdir(dir)
+
+ os.remove(tmp)
+ os.chdir(cwd)
+
+ try: dir = unicode(dir, 'mbcs')
+ except: pass
+ try:
+ from ctypes import windll
+ windll.kernel32.SetFileAttributesW(dir, 2)
+ except:
+ pass
+
+def test(dir):
+ try:
+ os.stat(join(dir, 'waflib'))
+ return os.path.abspath(dir)
+ except OSError:
+ pass
+
+def find_lib():
+ src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
+ base, name = os.path.split(src)
+
+ #devs use $WAFDIR
+ w=test(os.environ.get('WAFDIR', ''))
+ if w: return w
+
+ #waf-light
+ if name.endswith('waf-light'):
+ w = test(base)
+ if w: return w
+ err('waf-light requires waflib -> export WAFDIR=/folder')
+
+ dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
+ for i in (INSTALL,'/usr','/usr/local','/opt'):
+ w = test(i + '/lib/' + dirname)
+ if w: return w
+
+ #waf-local
+ dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
+ w = test(dir)
+ if w: return w
+
+ #unpack
+ unpack_wafdir(dir, src)
+ return dir
+
+wafdir = find_lib()
+sys.path.insert(0, wafdir)
+
+if __name__ == '__main__':
+
+ from waflib import Scripting
+ Scripting.waf_entry_point(cwd, VERSION, wafdir)
+
+#==>
+#BZh91AY&SYqpF3’ÿÿÿ°#,Êÿÿÿÿÿÿÿÿÿÿÿ$‰Má ð#,E„(aüü÷=n„#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,>û7Ö>ÍÔÌë¡;lÖ†#,wuÉwÎîÚ6±õ˾ï^)OjV›î˾›—·wÙΗ;vØ.dù½õ}÷·Û½ºëÈK·R×DîÜòÞc6v5ëB‡­íîÎwݯ}šv·Ó¥ß:Þs!­›šÞú}õî‡{ÖÒ«½™Í½îû··†ƒëÞÝñ¶‰}÷ßwƒÞÇ3zeß}®#,#,#,Ð#/#,|ØÐÇ¥#,'Ý€x³eÌ:ö÷l½ÝÞǪr÷]®6ƒM#,Pu×;>¶N•4#,#,Ð4i­u¡Jíš#0˜B‚¡)RvÐWc[H¡BI#,%QB…#/ìA½ô¼÷½Ü}°ç“Þ¸ÏZ;ž«{t xt©Q¶ZÅ+nµ»wÛz”U«#,m«Ï·»ÑFíï]®í×½ë«ìÉr÷nÛwzz÷—Ïi:w{¼7»36ﻲÜ7{Î^Ð4yÍY³Z@zÝ”+:ÀVRîíÍw{͵Ýídz»¹¶ë¥v×¼èõ†ŠÄ‘ osw—¼#/#0#,••W°5N„èP14¢”·½½OKšÛ&ö#/(û{Êóï·ÞùëÞí÷°òt@}zp”]mkÖu¾ƒ±€w×(Ø#/ôÞ-¦žÞï@gw½†ñÜèJŸ]Þ:}õ{kzç¬{ÝÔ#0”ß{Ǫ¥÷Úö^.Ëí Îû¥—Œû8Ùm-ºí·j]]7·+¨¶Ùí÷{Þå¹·ÓWžœwQ}êë>Ë&Ý]õÂê½µ'ÓíŽO‰ÞÞÙOí=íÔí»®9ióÔ½Ît®jîgÏ›ëï{¼Ù¶Ýw[}0îÙÜX÷»­ë^ÛOq½.ÈSvÛ.iÉ¡²§wg¼Ì#,{W/yÞƒìôÓîð#/îÔ¢”¢P#0´›#0ÐxWh9¡õ׬ÕíÝÛܦåjéÕ°YŒží™Ðõ^¥êí#0Ý.x{°KÞ½¶Á¡îíµ5îËÀ#,;zêy#,{«—#»ÎêãœæÎ=ìÛ¶wgKe³œlyÈî(íÍ]i ºk kâtôyzî){ºí95ΈC­C·ÙÇÝ^yõëÞÁðV¸ïU—¶oC¤òõ)í¤kÁO6övaϯ÷³ë¾³Þö×/o¦_ÅÈ÷}»Ã“Û»»mwÉ×Þ×>×iµvï>éó+[´f0>‰iô'sŒ®»uë`™´AÎ<XïZ®»]›ÞóÝ^9žÄ#,€ëVÙ#,½½e=»·xèÓhÁݳ·§}Íõ÷#/o®Ûkzw½+}Ø=·^ó)ê›Tù݆÷¾n¸ôu¯¤¨#,#,•µz:xŠë6ïXsÜÖ#0P!Ý¢æº÷­—p/`7gÐ:z ìo¶÷uÐ0#, œÕö¶úšÞ[#,}Ø­ØÒ¨+¬õ»Þînìaé®[öíÜÕÆ·ºÜ*õÞôÍ®vÕÛ;£’±)%í·tÛ$ÖS®îg½Ô÷Œ»SnÛ‰ë]²oMÛync#/|ãÇa÷ÎË»²¹·ÞÏ‚>ÆÙ†F·3‡6n«¶åGж¾í÷£ë˾û¨}¶îív—›_{¾­¾O½Žù—ðÓD#,@#,hÂ4#,dÉ©…!MG’=O(µ¨=Aé=2‚S@„ @&1i ©ìŒB&54ÚA ÚƒF@#,#,#,#,#,$" ‘L€TýOMTýOÕOMˆ¦=Êz˜Œ†Â`ƒ#,“Õ(¢AÚ¦12'¢ž˜DÓÔÈz#/ M4ƒ@#,#,#,#,#,#,"H4Ðh#,™4#,˜4‡ #,§¡’e£@Ñ #,#,#,#,$ÔD&€@&M4ѤƅSò§äôžªzE?#ÕGêe==1Iå=M#,#,#,ÿõö_çÕZq ÿRÕÍ[?b«]Ԧȓêª×p6dÑ£ ±LUEŠ#0x Öcïö~sù¸ÿ”~qDIRMQv–ÜV.ægÑss…‡˜ª¬QŒ!òP‰þf¸ŽÃ Ð †$ /#,xvd;ÞçºÈørówR­#0쨰’RœM^.+Æ/]ÔÕ§|¯m('ݺ¾&âñˆŒI)wWUi«V6ÛIk[¤E$ P¸EU* ‚º-`I#,)™I ®@$1‚‹xD r $ zâ´@ Š@@ˆU# 2*^ ª"d­UL†1™±¦f–D ”Fi4lÍDL•–2"ÊQ¥ÛI°ÅLÉ4Œš¦¢¨Ða¤°SHf061’Ó( F¥#,i‹*mH¤Ù-¢))e¥)µ e¤ (& £61­’¨%¢4¦™„ÔŒ”4SJ‘#)±´l&´­´¶Õ!Lµ Ø“Fbd„Ñe’´ÚÑšZ©)52T¶Á6U2jZ™i,˜™%EFÊÙ‹%È£D‰&ÑJkhØ`…‚Å!¦c4¨ˆ5)£#,M#2ŒPÈŒ’l6JÒK"³(d HH†’šVhɉ£aˆ¥‹RTX Ô0©²A5)L#/ ¤ÈIJhC11RQ¤KF’i$ÒbH±dfSS¦@ج–± °Ì©&1#0lk&VI&*6(#/D”D% )±A!0”1’R,²fdÔjeDFЛ%HI0"Rl„IJÁˆ1e™ƒfÒF&‰‚6 –$ˆ¥°”$ͲÌde,–1²A¦™bˆMŠK$lQ*BfAÊX(ÐT³ X“BT’ÊL Æ£IšEE ŒÓDMT#@„Ë#Idb6JL‰ še#*4fk)˜¨6†S&I”Œh&„†d¡1@$Ê™šÓÛ)´É35"‘#0AšÊZaY"ÀP’D”D–,›6FÓ4kÈ)3$d¥‘6m0mˆÁLÉCfM)’†3e4EJ$¢ÍK)I”RFLI‘JɢɦÊA@Š,V RI‰$Êj6M"FÆ„“Y¤Ãh4lf “!,™FH£LÈbH%›%2)DBZBj›6-¡2 II£l‚e²)Ècc ”T‘Q`•Œ¥&ÆhhI¤¢™K"%0Å,±¦X#/F‘(Ê2Ñ5jmkFÄ’Bi”R™(¤™‘´%±)f1I‘”Í,©f‚ÔÍ `”RÄB–2‘¤)¥¯â6º2+HHJ”F[¨ÚÆÑQŠ“&YQ#/’hÐÒÐmÙ&£k"0´ÉˆdÙL©#YLLÍ3Y5cJk%!hÁJ,J#hX,™)iL™3dØ,YVš4ÙB+eHªU+0¦ÍŒÆRÖÅ‘Ù4ÊÈ•%™µ’¬¶R”Ó#/¬ŠÈŠÑ›KZJe6ÆÛ¶ƵTm´&5%Tm6M´M!TPT*űa›F¶l˜XŠ©#0PJ)ÑFÔ¢iµ+¶5"V1i %´’mkfÛiبHL¦TÕ“LÙ&–‘E$mª[J£ËM,¦ÓY¥6©¦#¥BÊ$›if©dTBÚ[2dÖXÐÍ2É­-K!k4ÄAC*4‘Æ(ØÀš01 H† Ð˜ÀËQjIQ†R¨¬“M&M%@)³DQR#0Ë#"Ê$…#0J6S4 T$¦ƒE¢¦HDRÓRb1¶,Í4"i!€ŠŒ˜ÃL$d´X! !&ÁBlQ¢K“4³$-L¬Á”RcL&T”•†J3*‚Sf’J3#0$) Y›DE3hØƢŠ²lÐ…-4…FC"4hÓ)¢JJYKBd¬a”Y$± h¦‹TlÖŒÐj*Yd٤ͥ“hª4  ’ÆAd’ÂT•¤T¦˜²AF[$¦Æ Û0©¤ÒXÍ%iFa¥6™µf34ÓS4‘ Œ¨" •4ÁƳBŬÒl”Ä11’’6£T&£Q¨ÑFØŠŒËa¤ÃI%#0IŠQlQ`M ¬²É²RA±hÈY)f’Ì hØH¶-Q&Š,µE‚"-%Q°Q†!aŒU)BÚ-”³,…F‰"©‘¨"-"ˆÊm¬Š”V+0MLʦ2¤¢J–%%ZƒmJ,•Œ)Š™¢Ù)+Z”Å%)¶CY,Z%›2 Ê‹ ‹LRQ’#²a#/FIA­™0b›%)³6&"•EQ™Y5’)4“QAŠ2š¤¶-”“E’ÑERejPQiM©MQ&£Uc)‹I­‘#,ÚcRQb‘JfÈ̉*DPlÍ™­b©4[F¤´Ó[C+b¨Ô…#/h¦k4ÛE£QcV2[Rde(²²±6£‹2ÆÔZT°¦ÙFXÔĤƒeFÅ•KclYš“¤µŠM²™š£Fц‘QIEm¶m¶šY,*a1&ÊM,©&Ô†$•b´[bÓ5mÖ³i6i–¶"ÉmMM²Ôi¶‰FQ@hÈÚIŒ#/eLÔFË"¢#/D˜’L¤™!lšÑd`ûüpÿ±þÿóU°þÔþ·¿ØÃGVjÿ©*«Ü†©\L·!– ?Za#0A`ªÂÊ`‰Sïp†g÷uI„A#0¥¦?õë‹.)ýÞòˆhó%qÿmkˆR (1:7"T°\0RH9"Mí¨27üZÚ%Ç&ü™ˆÿV ÏþŸuú‰%#0XåBV¤VP´}Zê“7¯Ra"‡|Ý°ÆôJͶ O[¬FºŠ¶…ÛRRM8nైþÉÜF£ŒœDHXTÓerµÇàW.飮ÉÞwA/–øñwßVKÖåv²~“©»¶¸7­ÇÏk˜«å_/EsoEÍÊ‹ "ÂfíºãD¤D…æ¡WRyÝ°Ëå¹²3zÜÒPX“6¥ˆ¬xâsŠªzoÿ…)ýö#016VŽ—™ªú»¾¦Ò;¨_‹·‹É¢#,X¾¯ö]áĦ\mâب¦¦Ð¤Ruß%cýndŠcJïVIh7DÇ#0!‡×¦˜À°gDØxº!l#0¡‚º4»ñ+ÇRÙúŒ!¤F,ë϶$¿÷¬a +Pì]ÄSñëËÂ+œè»›™)#0(¶H,X¨¾é}³  ʨŠOUQlö¤ñC#0ÍÚH1±ŒdqÃŽ´õÒä;÷©›\7¾¹x#0MS.‰mAF#i)&Kk±ª4Š4dƒ{œlË ÔÁžîb¬9bÜZ¨¥a7"rpaæ2N»¥æCVëºÿ^þîòù^ÔbÓâë¶F¼•Î—|}&ðE‚¾ê)ó©¯ñ8Ó4ÿ¡éƒø+&œh+û¨¦n“ž¸µ‚ #Š†À2õA­Bÿ¼;È›Éÿ9`Úœ¾”ææ+ƒf²Ù"aYHQôUtÞçS¦¦ïÏÏÍ5ºš%¼Ö\g²Ë ›ÓŠ:êd|>ÌîÀäd×ÂxxTt­´¨kŽÊõ% ðª‹>ŽñyWRÏ:í[p£“5B¶ ¡c„#0`…„dN‰”ÆIš}Ö¬£³z ]bE#0бá’íʱþÊ-“±0””ËCºÊPMŽïÆçænEæßlMùó”ÂH#/~Z‡Cf8¯µ—o~”ã+¬,‰·6ïóÕ÷y×Ã~ƒ¨d˜É/»®¢‘ŽOmŒ0éety"60ãFK”/üŽ7ì"žtw#˜”m¦Ÿ»¤£²vß^߉»ôW󮦋!bŒkÆæ’Æñclk_éþ×{1§é;&·ÂãAëlc÷=ß®ƒ¦j ¤%!V»ó~}ÔQd#/)¨¤ùèFX pqŠ3Õ`gfxY¦F#ÕË–梬^wåÞûÞte(ØÖMŸ7rÅoÛšëåØü½»aÓî}]·oŽúyä<ÅŠÅ“_cîo¨,–ünÂ5ôîÒ}:û¯Qž^y¿n´X2sRê’š`o¥Ï|Øýµ™(0ˆÄWJÚÍóBa&µÉ¹éKB\b±•@nït-$­è«h0ZJÒ¡l5dÂÈ¢ZP‚ÒBNÚ€qÖ”Ý ¼U ¾J)”©.ªÚUÛº¬R7ïמC.ýõnï$,îí»,"£sÊíM˜j…¦ZH##0uGJ» |¨¤:0¦M2Ò]5„²ór•S¶Š?‘”³‡ –ÅQŠœûjO41(Ü ©äûò ¡ì¢„õ_<bÃ܉GU”Ûrï‹æj°ã‚Qý®{çˆt°µNìUuâýxaÄ}QQy45Z5Ú” U%%0¢diKñ÷)~í=ÛSÔ FB#«ŸûYÒû.š>ÏTņÛá]Åë® ÔÑ)#0H‹@§&”~­èü<¹ß¦èàQ]ÉËϯ̓H2CF…lVÚTSo=²’\‰cr#wT#¯ØËa¢È˜Ù½AjC?<7wÆcK‹õÜÈš`£ÍÃOöktïªv(~š®œª¾ŒN/Š‰£GP§Tþ.wÏÞðlÀlšaŸeÖ붼£é/–8ä®fRÌq=öÆ$ñëÁµCݺ„r‰Ê=ײ–’$;â†q²ŽS™ã·mH7Œ iKx6qÑHYó¡Úýäؼ™Bõ:Òd0³œãË:vÕ};ÓCYÞ£é»3áï鶜­×X5£5I”¹&?J®åE·*§èÐ:a|œ•êö’Tƒ‡-jÃdkÛ&23쮸3³'©¶d±ÍÛô_ü}¹4 ‡#0ïºg‡fq„gb^ç„ùçÅ—"œpã=ºÑ[÷Bac:äC,dMǪf(=²ͯ7¤vˆüš+²ÅIÜ„zúe1¦Å4è=Fj/T÷D¢~Hq7á—ëµÍ±‰{òC؇o{³áÇI1ÐõUñç2N,ïΞ8)@èÐÄâŸ9Ê°©ÜBÖ³-ä°‹tæbGOÒCùi© :Îz<:öI^Ú¯ÞãÌe;Ì4Ï0<u/ëâŸFé==™ËÇõù_ŽÈI$‚±Cïq?HB?:am1Íû´omîQ5ya‰Ü "¸‚Qƒ…ûN9âjì›È‹„Üi_d#0?q»ÚæaZ<þ1"4ÀÙÕ–f÷fjLÝ:Và‘äͳO…“Å·#0¡­h<1H§˜#†¦…z~z>wÛžöõnlæjûé¸×¦fCd5DTÝ'Ý'Ï·Ÿ§Êüq:wÞ0ä€@î7DÁfHÝJb#0N$QK︅ÍÞ÷PKôzêmEFy4,qì¹~m UKTW¢ª#0(C•!¦¿‘Ÿ¯üMÍ!ËèÅžð‹ÔU&ªu”$1BApTôU¢!uP.¨_{="}nm~…W<"}-Z‘ûN_µöç P>ÝÒÉ?¶Õô[¸K¼G ³­+¼Þä\ _o桞ȷ5UÕÛ­èÁµ¬Ðk&0ŸLµ»Öf>«*ÏùÎÃÝ¥,ªEG£B:ÔŸJtpŒpgwKF}óMN|N \=¥ü CáW3ö’>"#/Œªû*Çr—ò‘<Ö½z#/4™(ú¨¤A_½0;vfÑEØÅXØÓbm&ˆÎ²ß[-‹á)ønhÇÏ5OžýyÁÄdA—Ž½pÁ†8Ìm®ÑD/QeÛ7ë»e1å÷M=<Fúˆn:9ñàr¹»jkA@îíu2•¹^ ŒT]~º–Ç é;wù=wÞÎ4;#06Ž”Å"“3Š»ÏèU&KïªGƒ5¸S×»k˜÷Ñ{bÈ°ƒèI±ÛZFf"(¨²˜Q:ýÒ‚ÛzÕRˆÍ:âÞXè‡õqÓn!­és9÷ãV)}h–ª©ŸEËN+É°g7TñœéÒ>ªwx¸‡ï¼®™wx²vhˆñˆ±'D¦½Y°DQ0ïgÁ…ØíéÇNuVn´yu¢#0h]ÚD´ñaK–RŠÄ:&Ù“sŒwôм¶Ù#0ã[E¹$$?ïWçÅ8h^é> ýÎY]LrtÈç¿ù 3ÎsËòŸ©U©#PüÞkÏ“êàÓùÅ֜ݰ§­y±º÷DcÙêg†I!”¸pkhÖñè'ÿ¬ízw^Û¼ëž9Œ «Ø&sαñ­…ÒCìÞ>o—Ûø•Na9zLy#¦ Ä_‰¡í>ÍX(E`ÅT#/™~ÔÖõ^FÆ*w_¥çßótÒ ”å"1ýU<Ò‚¶¯?3r(Œùžõ¾e‚ñÂ,ÎiH¢¬àPk¦¿–L‹¡©¸xÞiùûÎŒþÎ[Ñûx#`²DÜ\¦¼¹+#0åÛÛ€¸½ÏåR„¥y­„Ù_N¯ #09~ Þ‹WçqÕÝ&”É3ù<åÉKκUU¸ëÑgi†Ò™o-8y=öÈõ„Ä/±ZâÝŽýZ})·‘|ÿg´1Ÿ‡œÍòàè|ËU8°ÿ§lÔÂlj™²ïcÌs.a껧®ÿd|µleÜ5Ýþ‹•¤þ‘꿆œè¶ßh×_R2¸÷ñ‹¾$‚ÕF[µ2ÛñPbˆ,zðeŸÇú´ÛÝŠ'Ðüx«‚ÎÑ÷8NÍ‹,Nh°Î•wv!¶_Ò¯œöíϲ‹Ã²¼kÅ`© pŸ¨ÕQ¯;Ø}È &5[ s==œoÂg–F¢lÉŸ£Û}4ª"‡ÍP_Jì¤Ðm¦5P•XÔ±DÆâá4ØSòaå½s‚éHÒ"S‘])§ûùÕT91öC?¦©–9¢ÙŸèª·†subh4”ƒ.ÅSqç(’r˜ª)ç&<ùAV˜Ÿœ÷Ø÷Å|LŽ"<lÉæZ_g…W²Mþà Åל½~—ÝÇÊûïgSæy;Žî£òëaØåPô祃¯O— 3[YÁ¤-™g¥Ÿƒ=.q½µ‰^I_#0¥ÖÍ/†˜-Ju’MæzTasm2L¡7mßÍP!‘{{*ÍÞ7D­w¹S ZþÚúðyQÚÌ‘Q‹ŸÉÓ6õJª‰K«¢°l¥qpçÖƒ•@«ðW‚$¢}S°P‡d(ÁOV "‘â¡Qš-ètrª!Æ©,HnÕG¡}Ûä\Ž/r¶åi©}2y @3øÝÍ–h §žì Œ¹#/xL” ûâ&¼Cîç4Ç£°ˆ!~²µ¼„!_®Z>Í_HŇéPÐ/ÈÓ®(8¦ñ_rŒä¹û3‰Ö¥Cd®t1])ÏÙ#ý\Fs0cß.fF³ãºc1ÇRª…«îéa€UDYéON¡\óSߊ-Ç×R÷ÅßÜh,Ós1l@éEùê~_]Hìê12=Ϩ†%Å€R1}ÀaR‹w‘~PL®ýq0—'LÕZoÞë³ö@l€Xøo3­®)wÒÚÍJ{ª§Tfô•ÚÕ  ì™K¾Ôs¯Ã»m´&à†ÉºbèµF`L!@ˆÅŠÆ v^i¼"ã-ÅP0¹(õfŽiLÅ^q¢e…}­O™Ïà3¢0YµUDÝ%ú±`«!zÕtªba¢“V’e¡g½„ nϾNÕÚÜAnèýé⟺%½ak,õeÓÄ6&YÃŒ?A1¯^µ˜[·prþvW‘iÌJ3p>»Õß\NF8eÕÙ^Úð²˜hÓWt‹:5.»¨J>Ы^6õa™•#/&Àü[ÂSË.SˆÌjŠ‰¯äL• <ƒQ9©ÕÆÙäÖk+­ˆøYÃLLkQ(6H(7‰<÷ö|áWÅñ‚ꔬ©«ž<ÝÛ›Äír)qÕw']eçà±B*¬Q kLFéoï÷øYé¦~³Ûíø —QQŒÔ°íébIí*I'„]l^S¢Ð`ŸTÿòEÈI™Ã÷sÕ£\0Ø~ãö€&€ ÙO-çiÂÎñ´{qX= ”((ŽU§gû>)2rŒíQœöçQä‰_«‡ LÿƒdœÞñ9Øg^Ý*ñ ¢§šPüPin°š-íÌT‘åouDvòŒ©Ll›7¾JÝ-°í;:¡!šÌWXÈ5Ѿ‘§ldiªÍÜø«7º™¦’—6PÀîgá–¸iAºD†Àîù¸¥Ò³»˜^ü=ìû+­ ÊºzÇð†¡J`} ‹½q¶,ò Qä@‡®µ‚úZGû[UàúÁ­b?>ó\7Œ2ß„Ý­‘»ñF…’0/#0ªîõXüد…… ãVë,€|35ü±¯573jsy ø«{[òn#/ñG}ªËþMÑ/q.!Uëk1)™ múš®]:Æâú:Ç?%NóÊò06îTA³¸UEÏb¼*g•;Þó†§ÿˆ¶Tq(Æ.©‘Š‘WÐWQX²|ž¾Ì„šv³¤‡Åö½*ÖËðü³:ì‰nFá×vÞÇÃâí«¸Þ0¹šDéb#;Ì÷x<§n#/_”2© ­ªµB+½:kçy'ŠS#¼r‰d­í1Ö²ÙÕ¼]Э¤¤ÊX}¥š“néJÍ¿n1SÛ’Hú­ `7ç®»€m¡u³Éüšå¥ÖO»qñ1¢°>?ªƤî‡ßˆ ßÇN^p¤o—ûTÚ)•êÃ;{o|fȬšQm7§ïÍíéï׋ºZe§Ï›¤B#/âº<¦îï.è‡KŽ$ÛTÖ¥0£2‚[qò1@(`¹RNPuÛ‚ª0úZNtMeuf{v¿#˜¼Š…ÜW£ËËi<)È(r‡¹¤dãžÆ'À‘ðƒcQ©ªP÷¾)q—)¶Óâ×be+7Y=w¹ÙŸ#,$¶ÃÒ ž'Á·>ÇEËïåã®;m2ÿ$ÛÇîàÛ¦bÐÁ‘H–Š‡¢Ç¯‘QD´5wX¼—ÚÒí±YëF\ îÙcAÅxº5b;Õ¤¹ŒR$gÇSvCæ˜éqÓ Uwd×m»v|ù„”œÒÁnÒÑzÌŽêâ7m ÌÄèn·oMn7lßc uÔˆO¬O倳§³Eá4@•E(¡LLƒðVp1#³j¶ƒ+ô¸0ølæÐàÃú‘K› Öø‡aÎÄÇ%/¬·úm”$+U)ƳÃi®6ü}×À¡O¦dŠ@ˆÀ÷Ð%ÿÊv¶ðfÐHÐ9wÑ;EÝñ³ßriíåÿýúéÇ—UîÏýäg“¬˜ÜÛf@§]H 1 Rl ó ññÉ/$ÍÓÖ³ÓZãåÛÚ_n?63…ªp·–”0³32\×Mÿâ \rx®U1í¾á×›˜êí‡#0G?Aô¬çÊey?Ivôñc¬ð%^pÃÛ°ö3æ|·ÍKçìÏë›è#ª?g=Kmk·M˜-£Z’߬µë^£•ÅgֱʒaVlE³ã¬/÷ùd­ŠãÒiµD"[¼èÖÛ>4χ& ’$<â^(¬g—ß”ºˆ¹ÜÔ|ñ›ñã)Z£L”8ÕÌ:ñ '#/#uX¡¶(:xÅE‘j9*¹ÕÝ­wºÂ/¨H0#0Q–o¡Ëk©6³f…9èdó¸6J7¹_°,ªWXÞD%­˜8Œ¥Ø#/ÂéÓ[œÅl,îw#/Nš½×x±˜Ô7rx"g¤?#/^Ëòùöæî#9©ß8Ï»ûÃô'üfžÚ<ïËÙÿVþßw¿µÆˆ½©œ8#0žFõ{É”2ó¥Ê3Ö D¡à¨¼£¶IôŠ¯‰#/ón¬ùwüxíñ#,M›ÿkìK‡Ê‘ßîÿF#,%#,%"A/ôB®žD 6©Ê0]Þ*hµ–IV©¤¥½úÁHqt"<à#/2ðâ4g®iT»&æ;|ú<~’±ËÏhÔ#,OäTCg^Ÿ5Тžo£F—rÔb.´„ÝTHÑÈaNÐÎ×=Çñds#,L«5#/`B$`,€?«úwsòýû…ß³¾¸ßSû¹_/„ïã7“Ü{çÁ'Å bÚ{›L ÿÆØ“‚7#üÚ¬rài ï’"QâŸÎòÁŸöôÎoÐÕÉ:¢)‘{<êˆ`I ž£ÕIÒ1W~•j…ë@òâL°i/”@x¹ãZGUïä°šíômé¥ÿ Z¡ë„÷•ZÂz‰ç‡ö>d³GÖsl˜¸:=!E”P PE„ (¼z<£ò4‘áî×ßÀäŒßM*»À¾F0Ô Ú­` <4^@H . #, ^úR4sAâ#,wÆ¿#,÷BÚhôÇÛêÏ\³Óf¿ãú#,ÿŸ$>$|ñ5„aznà±ÍÿOÞAц6•‡¶½ªeŠÃá<ÐÖ¿¯ü3÷ý)†íZÿ|¸6#/»5Ïí–ÄLÑx:c-5]Q÷6;}}úN™ÿleøõ»Ùäx»mV`?²þ>šÔÌ^Ì°×RW{°"qÌ!Ÿ4iúÜÀY:ÇðÄÕ­gmÓÌ9=Ù´Uªº|#øÁøÛ°ŒÏ§“[øÚ_¯•ÎB@ÐÕ¼%2²áh²w4#/¾¿G·M=}Ô]¹M@H¸–È;Ü|×ÜÌn¡fŸ;y_ƒ¥fB°¥’?iÌÙ¯×1Ïto[%úYõ¿ÙË:Œ¾™jÅ„+sÁå.¡`¬?Ø»o@¥ê+ êìuuÓè’]|P00€VP…Ue{8ZˆRZµZÀ:<ñúDµf®¿˜y+¥Î°›Kôü¸ÅݶØI­ýÚ/=!­êŠ¯ædJ{(Z÷‘þO–öH˜c+¯>ÔJ(Ûb2xò`w‚ý³ì^s†fòM(Â3§Ñkù?{û°¥Æ˜vдãï±åý8†L!ož3+D«i%1áxª„×öFv`ÍNöG’E‹¿žC'@PŠ«ÅNTG„Тܧ&iïÆÈÎ~ß?mLÀ+ûþŸ#0jáT‚(Ž¢v\þ=˘Iåê¬ïC‘±¨ÀakÚ›;‡VÚÑ&¦ÃgÚ*˜Ïß<bÂÙ‡ºò„ ù¢³GíµáB+².àŽý‘ÐàºBbb/˜ÚØ ¤­Ï£µé·ó¼žäi6,##/=¹ûpnµé“Hå[=ú¸@c#0ÚÆŒÉmo"õ(ò¶œ§GöSšwk9ÇØÄaúó&õ¨û8¾z}¾ Ï'~„ÙþyØM»(gI¦‡æª—è –}ëé¿ëa<þ|ZÚáå}Ç4ÓŠ÷çkô<L¼“è¹cY¤Ü36¿H‰5ï|üÅö~‡Nš¾è;„‹›£#§-‹DóE2@·—!ÞÿK‹ºwi†Ã"‰zÏÙøÝ^DF ÉÞ<͹Å7½ kø,µj˜ÙwÄ°ÒMv&…{¸a‘ßü\$6œ45Jªi£ZFêZ£Qº!Ÿ»žü~¬Lüj‚#/ 9|BghßêÔ„%ôˆc„Ùº¸¨L¬{ã Ä]­å«É#\­uFŽEh†1ÝûgMñ7›¯î{ºZåq.F$D¦÷;Gi£ú6ÞÇúÄ’p;|;ðPe`ttGŠ«0<#0®Øpt¢™‚¡Oì}Wßð¿pßWÕû®†HTXB0,BEä‡c\I³Â­P™h6}òr»ƒi3LgÛ¯ª^ͱÓø¾Ü+kèmi¥¾n@±ÙÉÒ‚LU°íî¸õ¬CÔgøÄ:8›ÙÚ¡ÙîyÎôf"™X†t‘6–³­í½wø}æõ∆m¤ú#/Ä×Tê™ ¹Ld3êXÕÔã¾mdb!«ËÜøÞ^‘ïi1·“^óÓÁìtšPƹåjé<æ}×PhtûZ_ÉL˜=±Ž¯=´]ãÊŒH‹EYI³>ƒyÄé籊Ê<¶°oñþ?å#,cja„6t$$À€êÅ@¸øéŠr| ‡ó# Šò^sz¸çNÐ0ç,aèpŠl«ØÍ•è¥#,°B(Ý@‚à @M‡ßüØ"ƒû—OËm¾"•ÒøGº|æûk‰ñŠ¾ðt¢§ˆsÌ·9@kn±¤J ûûòg¼Ñƒó[¢#®«3ÎeÁÖª,‚¤êP!ËÝ@á(ç¬5؇Ž†C±ÒÍ:®Ä2›³¾“jÜB÷/V;hÎ/×Î~Âë]9†ågܾì´ÈÔ¼xï%`Ô½ÇǶ»ž¡´ãÙŽÄ݆?#0>Çšps½Jž#0ö¶Ì™²°+a%IE +QÉ–÷)8¹`¬Ù­…iž¿y³“<g¥¥¼ƒ„§tиøYE‡•òå›r.Q×Éü#ƒÐÌꆄÊJ[·’¶6 tb!ÍåÅÜz|ùË'<4C†w:BñwœÍV¦”n¶‰CÕeÆ©ñÕô¸Tü¯„H6_Ç]gÎp–:×­ôcvy¹ F9v©½Œ;•±·¢5nu'P ½dÜÛd3j]1K¢è%2:­#0k/OLU—6ú²‹•ä÷îã>ÿy ¶ê@&›K0?áï2eÌùÙ-WWùï!yþ²D³è——Iî–¥a+GĸÌÍ*€ pÜù™¿›•^šTr–×%±Á“Їúlß…iŸs#/Ž8°¶p¾(²Gº¢ñå\òa{µÝ狵ƧŽFb",st.mËFÅ'Õçê{~äâ¼#02¾ºzëÂùÔ¯Käßê¸A8f#0C:fHÝÓÊä7—»Ííåê’nÏHõg® M:!q)Di) qN„2p°a¼† Ùððï®y›«ÏœVÉF†ÖHØv½ªàÔèôrBi»§ 3ódè±Øì^ÕQGá.Á¹TFËNÍ Ðš±ü¤’…Å+c`X[!Š½±åuÍÛeå7“ïL;UCšSós¬ÙPÁÂI)·fp/2uAO7‘&#%#,À{¡ÔWN-­0B†TɃé #0DùW}« a‹„C&uΉ6¯6Gì1“H­þ’ojœ]vþQ¿¯¤à¥—fpÏ+úìÉ^yÈÕµs½ôÛ fLןo¶Õœ?XÇ­#~Œ×3Á·¾&‡cZÆõ?¹©‰£C5œ*јτš乺ͱÖÌRÐå&☦À:+Š:™Á.ÅT4nL< ͤP©PTˆi¨u¸ÑŒ+`0ÃáhÐCˆ3#,ÑZJ¸!3E ÎXÛd1‘,䉇1>»ÒO——ã¿ ÆýuÈ£gWÔ¶á‹x®š½bÕ÷•çÏ[rئE©SQWÀ4”hm%bá€ìÆ2P1‰dëk–¾VæñôZê[dÛcfÆï™åñêÝ>x8ÌîFÿn 8¿¬ÀVÉÇ\?qËEŒl`Í:Ž“Ë)g¨F·‹ÙüÆP¥¤‚µ‘ú0ñ ÷gÃcÙõ°WPª7žƒ¡<;#/s°üNί¥ý§ª|ú“^º€Òáú…®„éŽÑp|`j^í¸æÇo[D#0xªu–¥]NK2ÏòÐy]äæºó]ƒ\Ë»B}OilPÅ:¢WQO2w°&v>W{Șp\p¢ZKn2´EdliIh7$—Ö¦#/}[ž?¦¤Õf+Fª%.éTÍîR0¸Ð³Þ¡;µ\q}$‰kT}úgåP~u®1±c›>c…¨Aa›$H#ÃÖÀIñÚ(Ò¯+ "ÍõÊQUwÈ•L¿½U0„é©—’vlŽwÚêF0µ'††H“6ßÉo”+wÖÒ5ƒ<ärÿgŽ·²loîË¿¦ã5~Ý#/f|L¤’Pq5ÑÒÚ.vi¤¥sºp¤ãü´÷Kq>³˜„A#/ÊD8êØÑ‚¬ìz³¦ ž?Žð㈙zX¸}…f‰VMc¡ºêtì«ðšÑS*³Hƒãd E­êÚ¢0D gæ=§ôàá8äm…›þœimð–¬´<¯À«ŠRGœ5…E;%#?ò³ŒŒÛgO”¨Ó;&·7u‘cã\4Úµ|rmZ_áèÔЄˆ•ž™ÜÕ‘aÕÊósB(©¥4ŒÖIC"Ì'V”¤4'Ö°lZÉeJFÊ”SDÓ #,YRE±˜C?¡h'2 ¸ÄV#\²A¢‚Ö¨o.Ø…M¨©4iX,4 £`Í&Ó4<‚€ÍhÓQ©«#0x‰(wÉa#/Hx²Kú\ì.öù¡¦°ÞF‹= ê˜(qù«#,Ó÷¨¡§njëÍUwÎ)#Ⱥb(Ò<K³<;G.’ ð›véšÌÝÝÞÊFÅ\徺Į0äN¥&ŸÍÊ‘uGž#–œ©˜fŸÌ-S(Çõ}ï]ìgÏ;‰†n¶K-ÕÍr³`°Sb».¬ÖfÏVoííì†#/˜ì-4ï ›ÍW)¼tåF¿3rƒjq9Ð0I;c­Ó2ÅÇ‘ÖpÅ=hì`•jî[vcxŠ|¯ù^¸Y)2'çURœ‚†Jl~åkx¤ØÆ(QKSoÃèw0Q²÷K¥O“ ±³íM€~3µÀotgRÉÅŃCØŽ2V €¾qÂå—jI&ËFs#/ê‹Þ&r_\xßQ(P^@'3 †ƒ:Î5©ïplfÍ„§ùvîLõ‡í_¶bøð.ÀiëRM¦EFR¦yI¬v×K©EJ¸”†¤c´GÒò—¦Ï³~KnœŒN9:cKk*RDÐî×1‰x÷â>kºÛܹ#,Cêë‚ž ââËaC?ÅtfÛ—&U¤ˆÅ+5PÅ1{;*ÞËš|ÎÌ8„>Ol°Ñ#0¨Dë‚sÓoê×N#/,!÷=H•<`ù_ T÷bŒ9ñÇ¿#,©°ÿÞÌ-chBáŽEï6gl:ñþÎwý¦Ü@€íŠ¬b?nfuŠ‚J– öiÞ°ß ŽaMZ@U:ɬ 7¶ ¿ß‡†p/'Òõ+ᯡ"Í B~Alm#,ÕUKr­t©"×6¨!"5(ˆ‘ U$Òät¤Æ2Kg|ˆE:šnŒAÃ1‰¾u'^Ó3kP q 'O¼.V ø팱{?U¨å`F¡Ñë#0Oaì=A<³€ ÷ %#,3[ј;â[Xh-cãμ9W€ð-º ß>ð…òTä_[×W­Î¯Û ròLWçjRØ ”8fNjã4 ÐhóöêüµÐjíßúnæéA›jù”B–‘Ry`*=•IÛºŽA/$6xÿ~ü¾-£>øÍÏé½ÜÀØÇÓ© àTö&,#0±V#0 >¼mðM+¼‰ØU½ã#ÿ·0¥ÇöU¢\Æv©ùŒ7T¹&Ë_RT®È¤eT­¬SÓP¡IÝòÛÖû!áb´Fþÿ˜6nµHæÇ°`’Dz ÝóÃOÇ´f#,Iñt=£VïÝ¡ÝuU$›6úú¹¸Û:ÇVÞngý;,Ÿ?n?4ÀÙæ³<65l<~½ÚöÓ³ï×鮽—‹ ×xtE„F8Äl=šYIîjUà30qrB;™$)âÝX·ÏÄy3Iâ‰m£Ó;lŽÏGÍ¡¹8¼ðñ¯$Y‹*˜S†XÙp·_ÿ4F ú½Ÿ¿+÷u 8Lo‡WPUCñÏR¯%â–Û£Š¶£a–6Ý°¶#ìý{?2#,šSGWþPÂø?–ÕŒjMŸ_ìüÙø€$çE P–ñìæ²ØUC#,ûG´¨ï¿Øš¶ôä)±àŸ™žÜI‰ü'–nEŠ€¨)"‹>'kAÍ©,Ÿr¹“²¥‰Eùœf&6*þBø;nÔQ¬·s®Ý5s^#/øq+(‹ùï6îeOÕü…À} ˆM%dRiG«¨,¦¢å¦ÛÚì Aþ$,L ³ë¾Ë4bÕãA*î»[©n[óלZ^ŠÃj=SnŸm}?f‡ÐýR°QeÑ äñþz”’‡ÑÓíÿƒ°¼ÎÚÂ9D£ŽF&Ì‚ŸzRCþƒ$’˜w½Àßä¥4I¢R4È‘õêæûÍ«ñÿ;«á‹IµìÛˆm½I­õª¯%µ^ïnîîÄ­~TÃöf¿ódûS²íª¡-¦KgŠY„ L˜Pìî<Ë~…5/ß0S²ô'ë$>¢e^—ü¬^mÝÿxË4¾m:—÷Oé‹÷©Jg=g÷´º˜`ìB+{,˜c„­vÿ™(ëªo©Qo`Öàè´ÁýÇ ²¹éGâq8žQG8ì$Øeëö¾Âkߧ¤ëùhn 2#0èô ›‚ªŽhp˜¢„Y0¾²}ék††#0ƒÑã§ʹ<_#,”ÊT(D†óÜšoXdQP‰¨–¼BĪ{*óA×¥ãüa›{+ÐÃÝñIŠ…‡@Qéµíaù[HnìéU„…úªM&*ÄIúR˜KB\æßÊeåBæ`¤¶\è=hï\wùþš7‹O»á>ê¿ÿÉßßú'Ø#/>/‹Þ#DyâÞŽ†`,6yópãgîTÇø[!™W¬=<+¾ÅÏåÊä$j?YÑïØ"h>s]bæ³:åÄ.ã}nú$¯ƾ®@0¤[’±›«ó‹€ä 9m™"ðë›Ì¹íûiUuF®+1ÈdóE³¿4Òç¿÷AGêçå°Ø–!c²³ðû…3YõD.Ö_ÃjèzÊןÅݱö6ª['~y‡PðÌ Õg#0³°àí½HÛVøy7R_%TÏÝS#åP•ðcLî <»oR÷¨cvœ·×u´[m4ÖßÊ@Z#/À€kÒ~K ¬šÂÖ]!¶ä@ò»oÖ“‹³9ÎäÌïg=“C˜t®ËÈà\£Nù2]†\]ÖhC®#0Bª§½^&2b@xÕ.H»i!,)óü*XœˆúHòœ†÷ì{ÊoG#0ßD²¡_ËfÄr|Ö|wþOÐdúÏRÆ©Íy'åÏÌp.wâ£ØcX¦Ëëûûì#/IÕ–1‘¨Ñ»ó›iØDœþ=‹œâÖæßÍBü¬ô|³L‡RÓÓ¤éuyä9:J’œ`Ì;”Z*©¢³0ÎBâN*W˜ýß“˜?¯}.æ#¢õ92 ù¾ºàT¥ŸHzN"@'QÏ%Wð·Ñ!_Ú®LL;BDÚ×3”`#1¢#È™x>õX‰}ÏÖø!PP`À]Ì#,Ô7çÅ™¦ÿð³éU‚b°™5øZ3M¡ ˜Hl}yšzüa>Rf¶/6xCì=çaÑ)Â6/݃\ œįB°Ë‡AË/…kóæÏócù°ñû;U}:PØ°§ÈâÙ¹jêžur·:ëOÏù¿Jn<xšzf7ñåÏTþ¥aü™ÛôK€C„þe+÷ñê©5¯¸XÂ’Ø2S#,¹BýéÖ,ºB#/âŽe}ÙVXâz_hÈ8˜|;™™u.yjÕØüuŸŸ¯¶^oÏóÌùšù¹`zz¨=ü˜ùªùµ—說pá_–ﺿ|-«Ï›0X.º þ#0[Uuß:›Ôȳòö¯£«òÙ§uÚnkc•Ùº>Øvd0Ù«â¦ý–ˆQåyaä¿v|d¢vÖöþÓŒ1ËYoòkëã/ë×|3WC­á‰Ãf.‡&Ç_ºMåVÊE.êÂ?vù|7ýüí—¨X>×ìÇŽYù·[ò¼é–÷ÍmáoGuy¬}\ÓÏÉâÊÚ¾LmÀwÝ‹Ç“›:O÷'§ùÏ×ß ¾Äz òHè??Èì`½zÏ͆|òžÃ]܃çznÃÝÔñÅMrâ9EÉåçrxꋶWÊ·óÏ«t›F/kÿ([ŒïruS7‘ÔmK[eáøèßGsýNšÝ»›·“ùʲ÷æ¦Ã4;çÃFœÑ¾·ÐgŽ§þìw°ìG:žA[ºåUEÚ®æjã› Ê.6n–e¸,í®­3²ÎiîP5õ¿¥…XW-Yet͹GÉ#0†Üó͈(¸Xç‰çØ!0‹•×…¼4d\çs+šg¡É›Õ^Y滼œ7]ª®ï7|Zöy­DÛ,5üOò?W§lûu]m'Íűݳ;CÄ¡‡1`}5ÎÞ= #/üifo-rçèÐÕ&|:@âôñIX‰Çz¡aŸý®¬Çg¿;<o!óÍÂ\¯×Ÿ‡´Zëã™]ø<kù‚gO/Ÿ•z¿?o—È=nŇ»–«©8„ÃyØ)¤e^¸W톰ªú+¿±úsx´][vfOÊë"®­|c^v+è³dºpGç\lÿ¬ÈWö€ÊÑ'L;¾y;Ï« £Ó#/3—"úÝê¢XÔ~Þú¯ñì³#0(äý”HŠèœ—¯Åñ¶}·ü_ëüóâ=WÇa¿äOçîÍ#U~]‹óãtèÜù6»%äl¾#,žïŠ6ü‡W­ÂcEöC?‚Ç·“ûµøãƒZrBà+JX^k¼_W®ÏÚåôÖÏí­îv-oLZžÎIÕ9^¯íæö3½wɯN ¯y^ö¤$¿ìèÝøe™+r2*wÞ/‡³;¥Ë²›)gÁéþ”«æÒ9xapøÏñ>ÿ“äúÛ÷¿?nmUÝË#,1v>Ÿ<÷ùs·áú€ò+õ·ƒTÞNnöþ«üñaúYD±óoP±cTwyÑX‡ûÇ×Û›ÀÞýáR).ÖE¹ÄYÅ\ê 0ˆ~¿ƒØÐ2cÄ#0ŒŠÑ+(Vb¦—l\ÛÙïÏ´ ¾Oפo§gP¿Ý§èõp»†cðùý¾>ŒzD¼ÃS3iûúU¸x§¢¨h;ëé~æJ“æltgÓòg·“é5º2‡Ö½h:ÈÝ0ý·rŽÞ¿Dú?<ûÅß7W zGâ7qáÇÅÚ?xP·ëÚ~q#,<xhÔ€(ý»Îô“úÇpö~俈ù¾¯Æ<”Ëã•î#/|ºÛežÙŒÂc?‰)#/Êç#0Ý^å[¦=Ÿ°ø¿†¬Éì–qAÙû<|YĤ?GÃ׿fà>÷üçÊ•§Ofö¼ž{Êô½€è?Y 톶ˆaL§ø±4øá]žÑí-¢ìW¸·úß9òáèÀé߆¾ïÓ³Ÿ°nkãÄrÿ MÒö+ö·bŽÎ¾EÏÊïÝ=õL™/¼öÎÊx¦Š=àŽÿÛ󾸊Ñâé…>Ã=þI‹­ù5t÷ôñHh~ÜÃK¯¨SÓ”ÈØY#,(ÇiPœZ„$Ý#0*GÔÁ„kÍ.S£z¥DÐMÕ8›0@(ó—xsTh¬@2 åIÈè+«Xõ×fƒË†í¥Q°|aÃ×iƒ²Ë5|×վἠê#­J‘ÐÁ€ÐúµÆñ©þ­òK+¢cÂŽ5m]®¼‹D½3$°upÃ`†€2øÓ_ß»>¿óÿ‡¤&ᕯôëûÅÞ¾¼ð€$Ìv9QÀ©Û +äÔPÁTÊ}@¯{áJ¶ÉÎiS%aMŒÑÞ9!»®3Â%õc›'Ï^øãWÁ¶²óÎ7vL­«Fðæ’0®ª0✼Ž{g~":Ç/ÌõXëGHc€Ã^œØýGÖSÕ<“u9B£¶õ®#/Z‰#0ßd´´ÁŠˆ „“¦ºý¡i^°IúýòØ’¯<5G5é<)f!´æAŽm"í¢«#/”G ï—sÀŠ¸fò_WcÆh£ÇÆG—ÇeÖÆ¿h’CÅPBJ“(ѯd@pí:N2Ì"ö\Á ™|Ñ«Ÿož i÷åð¶ñg¿˜ÆB½~g÷¿=°ž>úº;æñÔ:½uÔpçOVgŽíYë„õ7etªu' )ø3Ùóqä#‹Ôéh¨óã2]?[¯˜ÃQÓ£@_§_.Š» m¦ªg]Õ~Ç/Çö4©zÝÃnjÆõÓ:DòäL™ŸØXÙmfÙ‡KâÙýŸ.ðã캈俷Ê57&à/cÜ4ÇÍ©µ0ð<æý6.w<¯åÃÐÏ÷ßñâ5ÛÆí¾{¼±!¶)’M5:³>ŽgvA°u㺪ÝdÆ6„ßÉø:•z/º]¿®yŒ|ã#,Ý (Ô(6I]ÍBI.áp»Iü|óWóý•¥\™†Dwy·_ˆuBd”òñËäófÉþå䆻[!¬¾Z€üñw~›>oòù8€3'ÌŸÁÌT¨°|®ÄkZ¹‘µ[r±ž˜©cˆ#Õ"ü^5²î£ÚÝkki±aA„Ô¢¥nØ€Øá‘a ª­(8Ü#0K!tÙ®—qd0+5wrš³,ÍTthX&°‰WdÇ`a¬sZM†™&ŒBÈš¡#0Hìh4ÔF2¸ê‘è¯NhÓ}dµAa¼C"’’oû´·½-3ƒIp3D$môE¡=X­2m:ÐþïÝ{­èï¼ZÎP  ùþß1ø,?a)ŽÊ1K‰÷ÿÌZÔrZ‹×©ÊHXvXEÚLÃóüyé/áý[ô|œƒ;;>ýÈ4¤mŒ±Hèé@ŒÖaëд›ÔÈ6,*Q¢¦“P„5žÌAšDP•W`¥‘•Gõ*(ZLÓõeôàžél·ãËß}’ïêÍo@—ÝwLt›mdoéÉ}-Ê)p°fŸ1®¼ùád§5+Ç~ª˜oï¦ymÍ–_¯_“ÑâU«ìú»ñƒÇ(ýoß»OÒ:^šÆ›ÓŸòÐ-¾#>N“YJ¾[ bçyúÖUWQÌo®—y£„{³iý¡[¾ áYFO•(H’A¿{¾RënJÇØ;¹|ß»ð_|¼¹ôxs”K‚)_œnÞà†` R­Ÿ^æËS®îÛËÑ”9¯]Ç5’$?­nþNG9Ì÷+¦æ°0´0îæëºǧº=ñû<®yÐ"ëþQlG‹Úí¤s¼þ¾Û;Ý>oO?ò³›³–]„Ùö"tÝ_é?·?žbÉ,€˜ò£~’ƒ˜\†Ðƒˆò{UE1?O`(BÏ*Å‘lY>¼§š3KÄÅnB“jÑ"¶”ÁÊØ¥;d§•‚£Z!¡”p¢³ݶ4«M«»µÊT›"’»wÕæ½5î£#0’²JÎl¸åª•#0¡QB¥"•Š"ŽÂ× мµc0¬¶võThyÉÊÛÖ¡œÃ@Ù[dáŒ+QŽ¦)q¶"2An$Þnçƒ0¤c]¹ DÜŠ#0˜)F 70e[1M$`áÛWUÓd¦±Œtˆlƒ̉†f*‹H|AwRÚæpI C†ùœ2P6´&T(Æ£#0ï´K4øofµhlbbÉeC…N"G«zŠÝ&“ í4¥­,EdÈ«Š1Et¢¨Œ*íL¨Ç:ë%#ÆÉä#'J¸f̸ø!U†$†¤S¼$=?D80W’Í'D½êÔA|šà,Þ%„"øûUOÖ†ÿi×ŪÇÚËIŒKˆø°±ÍIöâï—áN³Ô:(PžLõóaèú¦žßÎ×åŸÉßv ‘ 1™ÞxÿeÂÜè¬>7øÚú}£‡›O.§Ÿ ι¢ž!’A5¯K&ã ¢•",#/擹 „ðöxÓf€óHð ÏbüÅô–l£#?}\þ5ihŸ—Ÿõš?¸üç ¸ ¬°Ðç.gáú>y>@ÿ{T!ÓÄUÈå€^b9 Fgí)ØAvc¾™ ~m\“R%Â#/Öj¨9äoM(3/ÁØ\Ë™AU$’̪ªÖu(úà‘Ö/ÉZFºÑãË·°D8G¼ï8!úÍ9o}Îü~5à%! ü/ô á8nQ"ÜÖMÛ‡“Z"bñm¶Æˆ ?Ì‚ð§'²Û É7‰.ÿ^{¿[•ñö5¾Û¨.‡FLèÇó9Þ2hnFDގ΄fm—‹ý7%°Æz7^›+wøM$—S?™¾á÷i÷oÞûª^ˆ˜wV<C´‰¯?<¬÷{”.bJ)F @(ø s/õ³cMµHØÉâ2DX†¢_ÌÅlä˦ómðèìQ c±J¬Q5UøGÑý>=¾.zùÇ?ÕØÓ#0·â%OOºÐRɆÒ0H±#ê5͇§$¡\èš ƒº+P®ãA1«†1‚I‘¡¥aÁ­ÚkE¢šÁp3µ5º 0Ë Â!<Ô¬Ó­uzè« Á­A‘¢ãŒÁàâfhPi12ŠÔX_¡èâÒñ/^F&!êš™RéÚh>±S&ß!°Y¿ËAeŠM°¬jÐÝú͆¬o*Ì©H—y—ƒdEX2&u|3%–è4ÐøÕE{z´ëkU·Ûp²&JjtÔ:¸lj@lQ ÙXº,#/ŒÓM£aÃ(ÔíJ¶Ë´ÎÌDé#/Ðá1¶'[C®Ù`Ø2麰!“® ƒ²QršsgßøÿN††²<0c#/m%åj…FõÞË…×t/ÀëÞ‹½mÕä“Û ðµmøÝï#,ÝL¸ða&#,°‹"1 Wðy¤=@©J#,B#0‚,è·g/Ž#/=_Ÿì²ü¿!ým‹áB§Ž³‚áX2#S ‹é”ÉÎö¾Û÷GIM0­Päƒ xCžÿóórM8#húwÓ‰ÆÕ v©DópÚ~f òúIó‘ó~“³FZ¹uùñ Ð è°;ŒÏPðôqqtDe–AbŒ1aiSýpÅQœiwxc۬Ϟ¡¢†äxEÍÆ—#/"$JN×R#ƒN@‘‘"Fêa#/fý–5[ÒDæ*#/3CXcý}a£Dž›¼ÎÖ1m@ØzuÀ¡Þloo‡4ìy´Rà`#Á EºZ Â# u6U"Ûw079µtª­BÊœ¶óþ?¿'læ!8Ÿ:¦”ŠM»ÍÂhŽÍ¶#,­o§ÏüãöyümÓW·ŸõßÆR¾{êw7>a,øöä÷à;|ŸË¾uuÔÀ†ÚŠ×Ýü Û‡ÞÃCÅ—Ž$ÐH¹i@¥-M.R67eÎc¹Âes Ù§G\ÆŠ]vã÷{¯™ºr¹•ë¾»¹Za Aæ Œi)#ouQ•NЈ긅4›Ž"FÜÁ‘´~ƒi‘TR5ÚqÇ#N/J·^Ê,‡#0:jÍÌïdÉŠbÊ™ÃÅ»Qå#0–•&`Y)”Hà#/i†ûÒ±`#0¶¨‘”©E4Ôê¨iRŠüZFUÌ­ñ‚¬2ˆêUÑçÑ#bïI‘cdãnLr::H8pÁ(Ì#,Ôœ„Mïs‹ñCªÔ…B·¾ ™3FZe±‹Àå€Ë“5H”§!àbƒhÒÞâ+lV¡J£È¤Ë$cD,¡X"«Ù{Ýö4‹u'·–U¦Q¢qš¨ÇÈÃýžþlF~— êzTÆÓýb'j¼æ&Ý7Èx[e…±xEi—̓¡¥ã³ÃN/UA2‚bõôQ#,šŽ¬­gê‚Qµ G#,Óê,ªÄÊ<|`æÎäñhv-§Ô>öÄ¡%üéÖƃo©«oMFä™L[Ù‚“¯-JÐä<UXN—QpÖ(,žÍêEëwqs4ë¯]? 6Ƹ#/Nük–j6äoj•sKqŒz±µZ$ÂÕ¸d€Ç­Vß=Gè–'¾:—¬ÌÙ¯rðÛ¾‹Ãç^,‡]à‰ v–8Ò“rÉFa¶o1H,X §¾° ïš¨ÎTglD?ïP†ü‹àØÿµš°¥Á±TãÚbÍ×a ùFcZ8ªƒ.!›XÒmÄam®M‡cÊaHá5¨0X‰Ìqغ·w!‡RÙpØ0V âþD¬?{8ן.6ì’yÙäuÕ˱gm™ÃY†-ÏWØ×W¾ãÎ,pjmG”í™ôG4ÈPÜì(2(ääÐi 0a2BC-»âÊÓ¬lÑ}#9ÛW»h 5®úðÔº6•ìmÃì1z•[È<­Uw“* îU—CA~¿*ì;®¹#/Öæñ¹e¿%¶ F ÁæÐÀÛÄ2£iJ-âß8¶MËk]+•øIh5¸Øn…å†æ{Žüí;Ð븣m¨ê:ìž"²o1,•½>çœÞ[;¦ß#/+¥Ô`*Úd1!¦3+X©qÑ~ˈ¬Øí §±ŠnšºÆ#sl)”4±\³X™ƒÄ5K§M˜œã1dášm–!‹Np^r*0ᾎI¾ÑKŒMd®—vÀ°­C4rç9$Iµa9M—™¡ËÃì÷«Æå_nŒVE¿M¾ÉÈÛ •4!ù‹u³Qçò¨ê\àîéöµÞž–š‡>w±4Bˆˆ¨r<DQ0þ3öw¶Ç»#-/Ž¬ì'Ò½fñ‡/1q˜Ñuib•5[+å³êÔdI̼9Ñ$@V#B~†TÞºc3OÀ¦eâÝÿ’Bâj÷m±”ý&qUþ(|Ü®_³¨‹YÄFàªÞn~#0 ·™¹ìY\,†" |6UÛÞ_9µ‘#|íRøÑ8»HR¸³‰BËç9>[<D¹¡;$¤W¦§(­•$x¸7Ì& Q€©ÛÏL™4?¯ž‡7£JéÇo àÞ0c#/{Ç9)—S¯úûD;®ñ_lQÞøS‹EÁA*XêÝ¢ðêؾ†ýWYɧÈBzó!èDêYΘ¬ÛêD?W5_Ôãí¹ 3¼Í)KKÀrtʼnFHÃïµíŒ„Cų8FŽ· “}`Òeºèá4ï§廆%Þßkt© ݽË’'¢tg#/]éõt¯!I&ãò§ml%Ù\aߣŸ¢èˆ°åýªSi ƒà­†Aç$ÊŒ\2™LÍÔÓ§žŒ÷Øœ¦ûÓtÚ¨)#0IS³Á‡#/¦Žn£",UƒiÂÕÇB‰œ+#0:r´„Û=XMν0bˆJDÂc»Jp„Ô‘M¶µ†«`”Æ6uÞÄÿå1Gqøì:Cšoïß–;Gefû;‰#‰¤%VðîïÚCå:¾5ñß’ñœÈÔ$=åc¢Ð¢7zCö±»Êa(œdQ*ÇkŸÄ·ÔgÕWêÀw|ì,Â:œH9dàÁt'o”;ç—Üið  "¥O¼ØmäòzšJì-üIVK‡‡Ücu#j…ŠòÇgn@›%<ž'>n×;Z‰ÈdòýPæ„ îóÖ€'{¢¡…@>ÏRº?£´´®QÛ‹ŽŸè®uÉ°>3âØíù™Ÿ­¾º#/;´¿ÀÆôªºèÍÀƒH¹9œ2Ùh@H~¿Ã…Æn~Pð{ uð g)¤Yàf4‰b¡å9ùx÷°”#Ùï^ñ</íR€`BŒ1,Ä* [K#,à‚}Óºë{éŸMW%·…#04† Ý­ÁðƒÛž ~~–}sì¯;â>ûÑú5d]YÒ}¿ ˆÃ4.¼ ahäÆ#úŠñ«¤¥Mr xó/<DF ¸s0¿Ä´Ô…2~pèÖN¼r‰½ê–#,r8#vèc¥Çå$Gh¹Æ§¾à©kvx7`M1sÖ.%ï–ÙÉ€ý»}.HÒu•s—]ŸèÖ8™8ýEïjïýæè I¼ön¡G^&LÅSå1ºùåÃyg²)IVì–\—¹#0Q´Á*Ãøiv@uá4p‰˜E^tÀ4x#"Û„œV{î>Vv<ŸK>»¼<䘉~ö€žÛVrs< ôŸÈ–+k¬xÍC»UôšB$*dQ+ë æØØSÛ­D÷:n3^Á÷‰ªž[k°/´ƒÈXnèë»ì£%üguI㵊Knî-7§÷ðTzÜ·c¼Ÿ%ž…Õ¼ôÛ–r|Â,‘ðã¤è|F—u}u ÛDÜ Ê$â½á2I©¼ðÍk.ËŽˆ¢MÄö?»£ï:õ–JIéªôAfÅÅ„tCC¸:f‡2cÓÄôã[ëµ›”Ð72^= 7رî¿Am>èë/àOOЉ´êiåÁš,ùÌc†ÈJõZk$ÿ^zePøˆ8£§Z#0u»6nÝ}È¥ñ—I2Ó™†¶>³4¾<†3^ñÇG»!2Ä‚n¢ú–ÕGW'>úÅ®¹‘ÿ§¯À?Ü›ÑCîÉ2ènCà‚Ç ü¢q´ð ü¹Ÿ|2Ó¬7,šã&Î;fÔ¥5¡œ’ïÁ4Ü ¼õ‡>x¤´ZaÐéÃôÔÖƒÁÄqãŸ~[Ý·^š:+Њ!‰mÞ¹¾#/aŒ¶Œ¥3–Ã}-¾…åˆÌµ´.Èòš¢žPä=Ž¿Í¹eòîõ3Æb"€ÌÖ.ÖÏ/Còw݆±ATz, Zk -QûÌü~^X9>mïÞ|ôBá..‰ÄªÄB…×µ|{/]Šzs”ìc”’OÛ—Ãy‚ݧ²èÐæ#/ödñ#/1¹Óå¹e—ñLF~Ø<W ÁƒÓ¾ô68HfÁ1GŠQ t!nL9(!TÈߌ#0®GÓ«qvNN_¡èíá5ðî]¼2ª]/ÏÐMŸ:=™¨üJÿ€ßo^ã×Tº¤¯ÌšÊ_Õ5³^çF]_»Ž:Ø/×#/özF‘‘Væ¿TëZ ¿ü:ôÚj/òô}W•C«Ç§]´kMκ‰¡ÝºÃ“²A¦ DV²4×ïç9ÞæCáüúhÏ”aŽ2]¬®½¬ÚùJ·…пEj$w¾Ù›z÷xâàúƦ°Ô’z¸ªÅ(#ÞŸÒ¥Yåû\/#/_oAsvN1š Iñ¥ž/­¥¾¸É4>XB‰xV®ÞÕ÷º1žnæý3§N†órf©'~ðóvóeMeZ~4[BnªÓWѧú¶ç÷MNîQáqcºL,ŠÄÒ°²2º¥_r ƒ•$³f"EM¬¸Q›}QÇ­cè÷­¼ã¦œãwÆa4‰µÒ‹Óï%ôÖ¼ÞÔm˜É××®%Ï7.ÝüH«´€¨f@øv-ölgMÃÜš<à‰ïæCWðRñ¿OEµÝrŽ¼yÄIÔãaµGˆEŠD•g&wj0q+J^ÉTÈUÌ©)JãtÃF%­@l¹Ur„Š™†vß·Qé6öh÷¦ÖÝ0lyëA(·77žË©ŠðлOéÝÌk>~øô«\‰ÎùòSHq«Ä?›Î®:¿\x²öweÉ#/¼ºjÆZó‰ŒîQë1ïž6«Ýæ)CKfýN÷ÙJ-Ñ)HÚ¨s'9€U#0ÂË_]B5C70lÛl“ìe¥÷ãl.a°S˜ÁL¡ÞW1!Å>È* vQ.C´Ôh¾±v’ç¤#/ˆ?˜zQj…çÌÙ[ÆXÞàÍ‚ˆ©&“Ú¯¬ fÕ9B·BóÍÕd|Ý<í¯FþyÏm°Û Y6Ï#,vljR<ð!\;ó¼Â ï/0ýÙ9<¬±16Y(.ÕéÎ!´ª vI“S+R€Ø‘€Æ¿‡).w÷VèoZŒ}ŽuSg¢‘ÍN'âRøt¶6¡/µͱ’Ñf6ÛfÆÛÔÛ{]í#œK±Ù{mæ˨:MÌ»ºººW¹=ÏRöý†^ˆþŸ´ï}»‰u#¯;LγהµÚaõ™–ˆwD" Õy,ØOãËÏR½±©ëNK'I<"ëΚC–¶OØ<UF)¾•Qá·íÒrZFù"TA8EÛ©fFVÛ›[ãhròÄ(ojuýì³"46º9ûTDñ®"[vkA¥Ýðˆ˜žT2ìØûòXþBö û›G{¯Ý!t–§Ëž#/{XÆk($ÍÉÝ4§AS“˳¡¢îgòùølñšÖtÍ)Ej:xvhï@õÓƒ|¿dØÎ,Ãàvã>\‡‘p-ã6Uã:¼56%vÚg‘\\ ‘[ ÏG¤»K¿éÔ‘Œ!_O ˆ{•H®Š<Í@hªŠ ßøôQÑj*ªGksÇGæ5`÷›%a¯#0§OtáK‹ÕmzØBƒqXº‚R€â£<—Ÿ*;ËŒÎÊ®uƒR“²#/‹¥¾·hª1ÛdamªìñËî¢ Ö]Yv,ªj_À38V"µ×¶b©B6Š^ ž¯bËD²ƒÇ€„1+#S›—…mb—µÎ#/áôiª·ÉX^\%Üõéκæœ'þ¥Iª¬úƒ²ªkðͨ]ãŒ3×Z@æÂÇ`¥ëµØIœ)(©5ç‰vüØÅÃÌÊ3ëÝ\Æ&qÅøRÌ9ÚgÇ^±72«rn’âçápV8ûþzòçoïï'ƒsÜ;¾_Ž>¼ùÜÌöñ¯=0ÁÄÉ9›_uÒ¾3ÌùÙ#0Ÿ$j×U˜î[îqx±u:ª†º’ÐU†HëÄCË1¯O3íŠïÝp¢]¾±Âå86ÄÛ[ËW¤«Þc:T‡r¢Šì“;¼g²IH21!¼†®#/k³áœfvO«Êé¾,ÛYæ%Î-‰q)]L4Ãî—Œb/Û·Îgá4Œ꟎³ß;sŸ© ûè’™Iñw9ëåJ§ †•Q³ï§¼DHhãTêE+eY–Yí+€rÏý!ö*WGê¤÷ƒÉ õ4ÅF–¬ì5‘–ÖÂê¢*uóØ$ :ÐÙ>O‚´<¸3¹ßMýúã…S“… ŒÎ d­Õmâ"Œ"[«¬¾-’Á–Aœ*Xªé‹Ü.ÒÍš¡ùmsæôTñ¨ðj?¨÷¹!uƒiÊÆÇ6vÝR—¾8³u›¤ÅoqŸX÷f¡þÔºëâ®.ûð•Oº%1"]Ükœp³h R팄þ«KXL×"Šºn iø<”RzóôxݘmòýY<Ô¥Õê B7³Aµß›%…_é>«3‹NŽÏ<y½üj-ã½ï"Y¯žÏ4.3úê¼½üÙÍ?)k™—…à Ž Š›ÁÕu'ÓyÎÎÙû裙ÌË‚dŒáÉ[ù>¸éé×{,çO»ª^ð-š)AP`Áý’£²6”n¢ƒ¼€í†‘rénÒët·Ä1šÎç=ényÃN°'G ŒhVø”Â\bm0ÆGuÍç™ÊÝ·r®½èÔü æÿ*"}¾»UFü<JÂd^˶Ö{„¸XÁÀUÁL\ÂR5Ì2É°q2£>¼'É×4ll \íÒèYG#©ÑñàÀV¹‹#0Ý“š£»×ËÎóÎjÉ–}'UðÛXí¼É+í´®É.{£GA«±Î"²÷#0e š¦Ñ›\r†=2yto•93šqÄÔâ¬y(r`ü|·¿èŒi>Ñè¶^®u\úæö³<;Nzûo<ƾCÃôí±{9ÄÌ5³¼Ò‡ÔjÆ.~‚’!F÷¯…UÅö¼)4×ÅÏ¡‹7k²y7yŠ„;mšhÍš±P”Ä–:4GV³J¥]·]Ó¢9[ ƒ„‰8Φp“ LÚKrº«¼7¾TiJŒ#0,ƒ °$@#/KÜt@ ã3¨;ÈëÍ]Ô|/F£bŒNÓe¹é ¿d#Zµz7n–5‹Ã#0UHVñdZ*¸({?ÌMÕö k”¬B’:HßP²¾º[:ip>R%¢Õ0éÛJ刾U0¾µ¬ïµi.p5Žë»}ôu‚À3ÖÎYâ$•ÜèNŽ1Ùv-°f›–ø٦ʳ8cû®Åóè‘økTêñ‡Š÷/–÷º”b_ÈP6FoŒ3³2b϶=4ܸA‘‘oZXJR"H‘‚„ ß‘euyDte¼º• #0E„‘¼¿-°»T£áÛi/aöVðEìD^{ûçž0qò¯[®gÓ¥êï•,ljAÞžFzƒ8»SRnå‹][#/UODàî7†vŠ 7©Ô­¸Yã¦Sj]-Ö¼­xs²#/ úð\lLN¸B‹³®øadžªõÐ:¶ ‡So2Ì6MP+žçs#//p»âá8ZÑÆÏ^x&~Úƒ!?¡JÆ!C¦+jØ jÒ4ÆY¢Ë,(á²`;ZàcERs fàYðƆ LBôb¢ çªøκïg8ÚG»AÏêÂ×/G¤µ‹ý+š~*&\\—*›DU¦uÆl' üáóð›Ò®Kl:(NÙsö½RGÞ.ÅÛ¬òsíëx[æŽÿ–­!$)=yóÞ¦¸1·êÚ±ð½þ2@Ña—qÝ«Þù–w¹RîÕ³¶®¹Õ/Úô° ÑX:N¡²ƒ~¤77/Îíéœao¶õX›>4ê,CêÒ¶X½|’õb9áuk}Û\çWodAMÊœ¬È™î¨Äû½c‰{Æe8SµLg˜"du]\pZ»Ä—\j‹õêOÕ‹7ï…¼ÛÞÚÑ>þÆw·ìÒ¸¦‚MRË$ˆ¾#,XõªS4µµ+|cJöŒ %!]#‡{#0UâW4êv•ZÔJ`IpÄÜõ|åuJ™¢D,*,Cq<w¼pœ'CëIŽ]‹úñi½Ës "R«xw]”Xz–ð§¾)tóó³?›Ï™ë¼œíÛTˆæT°f·ƒÃ©ÍHòÛ#/*¶!3ƒŽ‰…ƒ…ÑTŸŠ¶tâ¹é{¹bë2àã$.DÊ÷#/ ©FŠ­p<ZÍ\#¢Œe#ÐÉçtÊP^¾Úyn¨ö[ÛlcB^Ïï7ìÕ&vÔ4¥¨Ù¶¿ÝXêÒ9m"Ôi¥a5ˆ«;¶<-eÏ…'ŒµµU¬$ÐÝÇm[Â*L¥J9-‚ò?ÓÒÓ0Ãõ¯ÚDD¤1íWž¨ktÊ(úW—²PŽ–椼L—iÁѬ®Ã#0[Z®˜ôGyåF+?EŠ=«Ï»,9~µ'_lºÃ¡×nñÑŒìaû˜ù²‹š¥E¼TÖ#0Šv”¸ð7J®W`@p(€@:œþ~¾®HI7öIFÇï½ÍZy‹™¶-\í«™ÎåŸl±·x9«Í6‹ê-¦?A!Ù| Ø=3Í!œ#,ªa%#/‚ÙvD[RªÅð ¤õ•®¤[uìo¯YÏþï=¼—– (!ß*jpM…(Ùibù+NSá%ÏÃ?˜Õ!vð*ŸËàK¯½ÑõÏõk«tæÓeMÔù`ð¡i %ZŒòææ¶ò•XY1Ϫ •»nû˜#/Ç- Øõ«Âœkpª“Mà—Ì+¯©r½½ò×RÌÒg˜=ónfbÒ$˺ÁD¶Éì°Ébh/þßn§|ÏVs¿›¹ðC­z§jñÒ{ób€ëÞ*'{3¾{MîåÄÄÑUIsdÃúáÏlôͱö!ù¨é7ÐY«dg1Ù6q’í*âÖTsYEr½ë[ê “r¼£@×îVœ˜5a}[¼váê„Å<½T`0:\ W/]²x(^§GhìôûÏõ×Ê nyò‹Ïj= ÚO‡wUÓ=n;Ë”{ëåâè½jy›/žÈ¿ÇöÓš_#/øç¶å®»íu][EyKpWjïŸÀ^ó{ûÊÎqQ2:='ºœþ~y:(Ùî:ÌKª ³’–p\`„¿b PÁ@UU!O6Ñ£gê*Óu'@ S‚³hô;ûÎyA"P¡=[ÎÓ¸r<ññt”EJûœä‘ì`½0â+ ¿EŽ•”¾Hžõ‚VP#,þE¯55ieBU¢sñçÏÕ#0¹Ý £¤ZÕP’Ú×–^&ÒË“®îtuˆ«úZ£=pí°=¬.èuNñYw0=QìŽaZ9…*„À¡¤šêŸ6m1¥êc\EAãÚ”Ëâß#0Èu¡P*ÚqÈ;\/›¦‰.2ÙtF[¨>ž¯ oj’‚àÈ*(,.r'·´£ ‹j…žCi%qòJx‚ê#§_8²¯¿äñ—wÈ2«ñøÖ‘¨I|Š.÷Ëź|”|¹ïÚ\¸iöøTÅ5päÏ]_Eßs*üë,²£p¢Äæjì! ‡º ©ðæ„òYȧ1ÌÅÍiå]q7¦_8Þ¤ÙM9G‡bö‘´X‡##ÅÊêÄÔÍ.®¨øYpžlV„FÂ0ÁÏ.#,‚¥“NáDö ²tw¥uÛQ3¯æ¡ÿ ½x¸;N;ÏÇ}Cd—û6øãËpüpM›ø©ñœÎHËÚ0Í(»œ˜Áµ7MñS©÷*;YîƒÎ1üºÒ;;¿h±,JaÝG°16»h¢¤b/b­Xªrl›C è%ž›ø^'¦Î¥Ž´´*;N¹õÁ垯õƒªšg=bxØ¢—¨Ý#8ž*ž3}ž®„±Í`5Ñ6Ä&]»*Þƒû#/Á°k:‘ϱ߾ïò$³®®náà#0±Ú øúM׿¶b¶ˆ.Y» _<3[V«`4˜CËvBf7ÂUîзÂ^OYæx¦.tÖjz¾Ú}sm+„êq£U΢§/’5OV°ìð‚StTŠŸ×‹o7RDþpøû|pÄ<箺‚&Þ\ör ׈\ZŸ¿O'j²"-‹[o†ÂkõôüŽ5{8çÅGœÒá²ctZ†™Ù±Ã×Ö»«©ã0±LŠ…"ˆy›t ){k3Ÿk ®†s¹Fäf|'7š””·Œóó »Ä:pt:8â}žøò71ξVâ;äx·:(åýQBJR~¯ð¶ž-zMªÙˆòò5te‰àÑ~Êô­ûÃ2óòöÒïÓl²:îÐç-Ô\Œø¤‹a¶C4æБÛp8sLîOe…i¿_koQXçL§ØÌ&‘¡œ®î¨âC¨ð¦”´ÅuéŒÝ|jw$éösK+#dæºwÜ“V¾ìcUãúãËv: ·;Ä._îqÏS¡¨E§p§†•ÞÚØ)iáxôc5^D7ªlKg8§ò «rU»‚™“RÍ'¢ãÃðÞ°Ü@°xWºNLšN¾­ÖXA|yÏ¿†o;Þ~ï#¬ Zg ¤c·IpôjϼðøWŸ#05ÆŠYmA`;mvÊ%×êæVùÑ{^ Ä£ ÛüµÙVžf@ögëÍZ õÒº¹ˆ(:M÷Z)—0VÏ‹¬#?·¹òؽgV€ðA4‚¾ªçcôMï€ ;¡W™TíT戊á­îÕä¶#Fd÷ÅÐîôpÍ·#,M¬¬)pn’ä!ð¨ôí÷Úƒ×é]ËS<kµaê©l[zŒyÖÞˆ1Î¥ÞÍ)ÞÏN¢˜tâ'T#0A|6$p¦VëÞsGïþ5±ô*8sÅi@ÔžÈfqšõSú'ìvÄÌÍùû=þø3ùEùÓ<ŒËv½“’~­Ìû‰-å#0æÅ’°¢²7±(ëÜ2›¯ #0*!|-l·.)V-*$štuaÑ‹õBšžÈ„-»K #0<¬ÕcZ€FNrÄ,½>få_ Ó‰ÑÏrùöÁº#,¸®?–ê¿ïwÄú¸tþSá)g^®<#0#,)òž’?DürùÁïèF|ji³€ûå(I«£Ã´” #0=!ð~Nî.›‰2ÏH^i‚‰úú}ÑŽeƒåçxÛ+auߊSãþò“XD…TBHMheŠ‰Hµ&Ù6(Ûh( Ìõæ,F$K@BˆNtäyüÞ4q‰µÅ-¿ ¹\ËžÁ>øæÃàLvr7+ú,mšŠÂ–’¯¥U¯Ú–³Puöêhü«#¤ÂtÉû~Ÿ#åNÕ|[=Z´~ôófGö¦6£ºJܘNÿ±²þ-ò†(´cçìòíî ‰‹'Ãïïci"_E£vL„Ë:Ú)TmWe_ŸÙ?”U0U)#0%2Ín?©ñþ­M§Ž-M~š·íR+c»‡éÍíðÃôó×O›ºéZ™­£Âí÷ŒÃꃲ±mtÈü\cƒ,r1#0#0‹ÃjèÚ9pÔu]6T>0mÜŒåà5ŸÕ…Ÿ‡ƒ'Ùò»S¡ZžuÜL3d5Âz#,jIvs%5.¹N¨88¥ª #0×Á„aðÁ\‘ùƒÌŽâ;Ç¡éBtˆ‚r6¡ ÷rŸåôã“‚#/³>ùç¬ÐþêˆbƒõK¤šõp²ßãpÁGÏé|S#0XSæŸ%£x‚Xv-êUY–ñDMC®Ûó/קàûÉW3Áq*ЃtÖ•‘¦U2;£c•#,?Cc”y6EÇŽyl üa­@pzo5‹.ÔG2ÖT›a~Ÿô;Äè‘éuÒ–!ÅujgÇ&ûu'R˜D J8QME‘L½Ôîã(‡ß=ÑÈ^ÖÞû#¤w|ÿ®Û#PPé3¡èˆ©èÖ„ùè`ie+YĽY*3ÜȦX ã¥aÓ5§Å”¹ÔÅÉ2$ 䚔 „ûΑ(º´£¸Ž–Ñ,¸ºù¨G×Ƭoé糿i‹m®w¸ŸÏ#,¨vD8Ïü¸XÓ¡ëÀhOSM~g勞_šÃß|Á²qñ›a#ê“o?5 áâ >öšk¸†í´31„Lk‰{ç![K–{aá¥ÀøÏ>°îØÍ~ŽI·éÓ»b9ñìÚf¤QYUÔoj¢Ê R/4 ~îÏ0lÈ I5*ø×\ÊÌi8†–å[øröŸQ1¼ïùï ݦ2K§H 9gÒÜJØcßVtp>-ÂM2Ðø_ŠýYÚÐ/–ïÕGg,÷˜€í\œzéÆñ5{ÕÏy œQ‹/EÌ@\ÛÜžK€ˆo)ÜCœtÿ%в…㶥fê‰ì _tOQR”#0ŒØŠ›énjïfQF™Ïˆevž|#åYÙ #,}S#,`Òã·º,IñƒŠ´u[2q8ƒ;ñöÌ¡¬Ö åîM ò˜´ 6µ©ò”CHàj‚Ql|AKhiƒæ%µÎ 5¶›ÊAh! ! NQøŠ¤ëKã¡x¹wœì(#0 „:Wk¾¼âpŽŠ7\_›f­M˜¢¼¦¢øp±Ä3 r‰¡,F³¢ [ÿ-º ýÿ›×cËb:í”Vxs;ò€^#”“·nè³Þîj80–ç%ÏW¿ ‚E±çôÐa3ÇòI)¹ã<`!h,vižH(PB Çbîœf)Î×àЀŠ©v»_A.qw.~¥¸°i6”Ó4Çé|´±8^ï;á8ðÁ'Á…|æ˜ñ1è³Fqv`‹QˆÄ$%d<… ©TœÊPZd~Q„ |€yì³U¼ÙÒ™ùUeÏWµ½ÇŒ§A]„!5<øk¬ƒ¾¨K!¶9bvÝ_ªÁG Q3V‚Û›TÀa€("2#0ë z¡P¯ƒÆ#/»2Äð€8"ùLŠirÕ žÍÑ ¸Z«\jÌ_2+Ú·‹³xňÀ“‡.€ð”ß6â®Vã–aeaÇÆ;eƒ€¢"d¡HÐŽ{€›É¶ š8ºKd„«S•3È]yXºmþnžñ›#,Fq“œÃÁ”5Öìp¸Ï@Î0›ÒAÙdùe|dsHjðû7ᾯ6Cuféá^Âî¶BÐÐ F¢C’ló-ùŠ¬M&X¤Óì”@¥m0žPÔ+ncXÃt‡Ôîa kSŸew| Ù"i’ ÌxJ2$FßE:G–\Ý1 ˆ©ê™›ö™owÑs,#Ë–ôœDt%§žD!Ì·ŸÇ~˜/+d§¦ú^Êö›Fá¾cѺ†#ßÀÜ “#/¶# Å€*,yy¡#, âBE”wåkƒƒòéµËª#ªíÊ5^Èô'r£Uö.ÆXŽDÚæc3ø£}Œöv×¾r/¿uÆs&u€•3²@@à‚#,£¤Úñ+²T‰‚Ój­wæ~2Ã<ó*zp×#/9ÚÖØ¿jýöQÃ*——!û³¹óü×¥û;¦,ô¯ÃËr.-­–?­2ïqP‰#/ÕÉ— Zlœ¢è ûù" x°ÛU‰b„T€C@MÀ(MOmø2¶s’ˆ3Þsº³ì®m·ìð~ Øõädœàéöðâ G%ÄZ4“j®A­²Øò‹˜Ct`ÎUë(3ç¦í¸¾iòÑe‰¸¤¦ÑÒþdWw,/òmì¥{9€Nãô‡5ÇŽ/m\¿ÒžÎÆDÅû® ¦#0—uù\ÈÚ«H6D&&¤”#!C;Ó§L@ÕÂeç5dž:}æÌuÕ gÓȸ?TªÆ¥@êÅ't¨ýÀ‡29« ™ŠŒ0vp汚…êßH#ÏkˆýG#,Ú˜Žï€‡îA ¨Ž‰îæòÇôãåöùa¥#oRßõád£­UÐ^­û1HäêLz!†Ò'­Á‘ZS@x¿!¿…wžÍ‹‡&‡·ºº<„ò'/_˜àÕ]sƱ’×v]NdTß#,Eä¹Xk&¥—ß]¶Dôöô|¥5‘±_ƒÃ"íýÙÛÒ²{‡ʤ—¤¿Øáì’ Ã^p?£·døê|ÍHÖÓí]'Ò÷Ã[Gß/y¸Žîrè|˸×嘼Züäm(ðÒ¦§^„¡—€šQùÄêÞ¸D®‰Æç'º©oñøâx>›dçL|²ŸjkªôL‰Lùëå=ž ê®G¡÷Œþ7­>m;¶«ás‹éžLÒ²^n7æ³½¿­AƬí{˜€Ä%ì¶xafsŸ£¯m·6ù4vbòÔ U¥Þm…m×Eqü¹,7Â-*P‰}ÚîŸ)’[qóåÙ·¯~šõ‚Ø+¸H¥O£ ÊkµÚ!@’ÞmËïÈÔ÷&3± 2¹]+aê8܉¦(·NeŽ/q¿×g.ÞQ{.) *ï˜D<ðY£I”L¢ªÚ6êä`#/ê+y1Û¨¡¾5z””1Šš´¢ÒE¦Å–"ÌcÍ0± ‰K«°T3Ñ#0#,2°ñ>@ãlpMh&IÊ ãi¤;;½½ϯHlÏO˜¨÷¼B-p&í(}/’‡‰bSö%?ºB¸’Sr1ÁNžÈ!=#0L¼¹>µZJz?Ô RSN?<OoMtˆš aþ¶[þúS-¥E˜ÚSk²åù“·jóÏ+ÌÄå#,¤’Pßö~íäfR¥4ð¢/¿9²0Y=·n†Cùà‡ù{AŠ#Wê÷úëG>º ý€ þq}+ñ'G—ÍŒáÐ}~Ï0aüàøI‡«O^DX¨ª° ¤ÿP´€Äÿž ¸?®@ÿ#÷þ÷ÍÀ7ýÆoäû¾Ë`yêµ<Óž~lÌ#,Éíé¤MpÆ!{Öâ9zÃ_¯ÛÛå5èÖ÷¯.:`)œÎíÚ›!ˆ~Fᵞ)ð_Z0$ÙU')ÐBÛ“jÙ,ü8z\˜èñÍÈwú¶XŸEz‰Â÷úú¥ËøU0¤ÞÄñé¾d¿#ßÝ`ÖÔŹ°„‰ ¥³ƒO21§N=½ÊŠ`7êÙª”,ÛÄ˜Ç Ç«{Ï“ôËž¦¼fOQó#,‡ #/¼ÃÍÕw1ïÉ™ËÒ{»­gq¸^¥ÏS 84—óv`Uwæ¡ ŽÜ"(€~#t„‡í̾q£YEõ÷þ[îUoð©AÁ‹6'Ñûl:r¨jøê7fRKfQ ìˆd_´v½7vCeãdò·£>§øÍ V€‰è)…à;qÉ?¯"³ñéü w»º¥L0[L?½4ÝþèoÚ„8Dð:— ¡è@¬J.‚È"7@Ì\gbaÉ(…˜zب˜Á¨Ž!}[8äYE‘ s-)²6R87#,¾€\À 0›)`?Š9ùå"Ü2M…’ÆD‘n—)nPY#LÊêU çºÂü\°RF{EÜ.v2+ „°#aÁ"P©†#0E„L •1CSẠ¨!œUþT([g°fCÊIóö*ŸïݧÅ}þ„>uAf<#,ÎABÀ—DÄó÷ËÏ›/ß½^?´'e_ ×ø¿±pŸYûš5<%Š–½UtZ#/à…Ñ0ïŸAø®ûŸÌþÖ§­˜Pž–˜È̆ËI31ÉÅÔ Ó´ˆú¨–+ç˜ïI®óûÝS؃Åÿ4›g:ËNšàãÃ9#,$Øn”‚÷Y<tÔ닼ÕúÌ TÁ{P ŒAb”ÉDª±‹tɶqpˆ›Í=urjRL(»kGeN÷»¿&¾#ßß®QŤ¶’çø*g„+–!þ6˜Xø5i²WbYÅGæ¬L}WiFÓIô §Dà¸Ù;<Æ c#R«.^æ]T7dðfŽªVPô¿áxyÖ‹L_-)ì´=àv´Ùæ·.Ø[ç“°³Èf¦—OGP´¤{àíۇ̇{=Šy”´ÐúÍ7Ÿ×5SX‡uŒ¸ÛKâ#/‡Ý`Ç&…ïìT®¯1$†'çƒÈÇ×¥ž.¬°½âщEAbÍE´i4Ùf´™˜A{ÛêzN¹9*ª‘¤”UBÎÝ~j#/ÚOƒrxF¸8†sÞÛqÉp #/ÂY—²·‹º ‰7Š^ùAJ‚Á’u$3Á’@„¢~c±_l\D¶ß†i3ãæÄýzPÆ=ç]¬l%;È0‚ŠTMº›ª6¸™s¥Ï@À9»á\7*îµZT7PÃ’¤†”èe†DòÚ_1rÙÍÐzÌØŸ‹H˜´ ëÎðP>á„ &Ø# És:P¸vVòl ;(ÆΆÊ8jQ°RÑSoA×ç¶Ìcì†XUäûzLØ©»YÄ7ÂK™c[ÈÓ³‰Â\#,ÊDô¼a!äyl‚`Âf 7ÓØŽÇuã‘œÛÂÇ©`b‰ÞVzhDÔ¦Šw'š=î–Dã#,çŒhŠ¥(Ú*˜Ä¨‹%kÒËi*ØÕ¨Û&¶ñ€Aˆ ,'¹Ù3^ði1ÒPE¦¥ã\Lˆ.Ú‡!.nÍžu„ž´”-‡ªšÜq"#,ÒÑãtÈt}Ø»÷$ßž#Á‘Ka’\ÕècÄáÉÙÖñ$Øè‘bÔóîMÌ|ðä…ëÛj'9(é³R†öãÕI¸Ç¡õuXgwKI ²m±²jÅ_©­õWÕ%ÃÁ qð/;ä¦já”óñÐ2lx7O#Ó°"¯xÖE("ÅÎî4 zÐr® nµ%,¤É$Œ·ž*˜RZøT)2Nyl[ý•¬Dû+ï±AyBdvfÆ1,˜H~ØCDÊ#,ЩˆÔA Š$ˆBlmÊÛê¶Þ2ù-«æ](HU ÒÁ9[@š‰ìÖ‡ž>68<xÐ#0ˆ´…QZíÙˆDbÂɵOŒlDçËlHFGœ5 ë‡ Mùó·¡½¸rF1"‡ŠŒ¡6Hjƒe&÷òW]¤RYVºˆçÔšY.SK€¢¶»‰lå o‘#0$àÔ:Éa("ç @Z<ÞZß–Þ9‹Øtr#/À ¶ñŒI#Àhõm©BòF;ya’bæ¬Tì×+Ã5”°ãD(‘H¦’4 ïçGJ³§°×SJ„ŠÈ75D#wG,~»<;1Ö±,= b˜<A4S¿u'˜ëÛ› ˆ÷î{[ì¿CÁÜãë(ú, Ê"¦ê¯xûTIö«©ÏÇ{ï™Õ]¹]K„÷¼›3UÝÒ‰h.…Ìg‰B€4ïš‹ç iO“œ' rÛÛ15§>á,KÜ{†“·Çf/cïk…"tÒËÍ­:Ô(ƒàE(#ºZÓ:ä†0¯U+D@„ ™ ²ÈpXd]ä#0¡ç·Íˆf‡3ŽÄj¨#0”O_§-]ûÏ5Ì–æ{B¡ka³~O§@ãß”Òð–sVI$˜NÀl˲! ²+)½Cü¿9°®½–«ÇÍ¥Ž2ìwbIêöwr{™N©Mn¶nOyGáåÝŒž4l„`¹‘°1šQ/]Ô‘ðïlwÌz^¾Ý÷u±§sá¼4=¼Bá÷D¨ûºÕÓ­Bá“…“·ÍÎó$œ>¦üNU<¦Ô$À,&ù4>¨gg£ž}Ož`æÃ;¶è@=.¼Lû·lï;+ÑÆõË£N8Ümàü€¯Fë5ŠšiF…&åø|ìÀX¿t¦7{Øê>°EâãéÔí³kÖÏ.šeVî83ÃÀ¥#ûfö†Â?ov&mt†wÙ‡nŠ¢á;¤%óï>;Ó lUŽD¬AXASêb@HH+›û#/<((ÙÉŒ½ºí!é`yÇ2ó î#/~Hû3ÌÃ-}¹NÚ6×nGæcPÃn¥°÷©ç“4V-ÒŠ2Nêâ Êf¬ç/‡‘f0Z ‘P›Ñ)PAÅ,˜&™ï‚ß÷-:­xÁÙ=héÔ³sÙ]ÉÂ|vÉÕ]¶ÍKéZökjlÑf½ÌûŸ7¾âÆÌ;%]ɼè}†žV½xLó" 7¤342<.ÂLSkiu>d#0ŠÊ§h;q}tÙ¤¢y°ÎÙMÝ,‹z=>/õ¸>’otÖ¹NZU@ŽÏ̤¾±^2û1ä¡Kg«}“èDŽǸ n³`ÏpjȤF0W!‰ÄéK7$Ù+¶Š‰ÂßqvŠž­u‹ jM•¤ãAËÜp7Qrs‚fšc½¾ÿõYÙ‘\·IT0í£¯R¡”0D=‰vCd÷màchÌåTªV4`rúÔM¸…˜`A<Ê#/ѯ‚@‡…èbÁ¦ëÓiË›èM²;¾û#/%9×C;Æ ¬íØÓàž„:&Úm²@îiÒ.¯íü I3`n836ì­$:'bá|âšë›;8{ï ^|œˆkÌ3“ÔŠÏ8Tv–´Šj¼†o¶g`3DvåÌ6UŽÃ.5ÕÖRã1ÈÔ†€ï"< õðºí#08Ç)P”G ƒcX›§SÉŒµ“ÊÐʶiW½ö\sŠjD4Ž¨B^pr,æ@!¾në¾ÖÞ-#14ZOZê¤ÒdÄͽµôµëWè/±×ËÆÅJZ¡q“\XšwÔÜöËRðq*\‚I$ ºqÃhâŇljô—£ð†£õXÛŽûÔ÷ì*5]6½ÙÞ|ê„ê¥ÑdEÔx”SáSÁd¢OJQ³ßUöŽ47a^8;I‰šO¡'49úŸ}tÁ¦zx¸@cßç± 37kÊî°hÄ•sŒà´¥ýŸ,!k“8%Ð;Ãç^£¢ÇGùÙ¦øTŸœ†êÙ@q›IAÃÙŸ_-{5Ã1uÙBþº &ÁåÓÇøô#/žÚ:ì]ÎDÀ}ЬJÉóÞ‡T•¤+Bé]u{*½ä³ÃôþLÃ#'Ñb|÷ìûjx€&8€&×®Ëá ¢—4[—ˆuß<B-ÉÀú4køE€ñõ“ô#,"âåTó0ó⟞’¡ÌÃ'Ó~φTwó;EsÎ;á,~‘”•ÝÿøâX¶³OÄ[~ßs#,ÅDßTÊ&{æÏ)±& Àú:¡ËòWÛü%ëî0˜aý³:@$“ù¿ÂN ²‘"€eËHZg‚ü_Fµ©ƒ¥I‚g"¼ôæs`Å’Ò=‚ò•ýƒ]]‰ NîÜ~¹‘Ñò‡ëo™ôôržå«,û³Óu¹E/ò?˜Ÿ¾Wo¶õ^Ö¸‚¯…uCÐDOÓðÿ7£ÃóþéíˆRïÖ$/ûÁŒéNUËIbû¨åZ¤çÂ**ŸÚ‚µ-es¥ç©=#‡j9ìcGþÐAE0íî2;´Ï F#,$o£NÖËVdŠ­U ÛW×4³ˆ3—ïhÖq§Õ󋽡JZGœä÷þÏÍ·Wå¬5BÜÿoߌçû9; ¥™ 6„ÍÏËããàž5Úy#/õ¬Þso÷°ä“ú’’"DCö4ÀÅ Ýü#/ˆÜ¡i)}Uàc þbñ³ðÚð}‘#/HçT?è²~­÷ü?@®‹ÉtÓ#0¨Û”ÃuÀÄ„{Ô YD‹eE]Ù­mÂw¸™æŒ.uBsºëC<PBÊç  L¹Dî=X\à€­$©åG9îÇE–g‘ö;ý-ÉÍÿ[‹Ôönö¤Åµ õtËgðs©+@7õcVïÃâ#,›sßðt#,Hg¾„2¢•‰ç>»X06M”Ÿ'ÚÒÅ(¬ìßõZ{qøÝy|iFÚ§×3ÆèÄÓÑ4ˆúœÏ®zcBzHjëE#Y}ÒÏ—»ãÇ躞tGàU)n1ïå÷I×¢ Ÿ&˜0攲,ˆBF S"Pä/ðå´:?ÛÜwøËï8y¯ê´ËÞ_º»/Ó¥_æuY Iú¾/E‡º'„þãnZ˜ÿ”?Þgµ‡Ñ'ø“[¥ŒDH±d?ÊrãÏá§ß}#0ªæ-]Š*¢%$âA‰Ç>L*ûÄäág—§ [ò#/6üü6ü?´ÏÏ›Gˆ½=™úçZ‘uT<§ÏG§Ømá˜qñxG£¹Âεë/#,RY—p#, ¶ ÿA¤›_'áIÊAåîߌØÝ>s¿ù «‚˜¶#0¦J¼fµQœ¯äª‘{”€Jy¸6®n#,@š¹;l)cÔˆ…s­æ#0;(³îû>€äLzIëì@ÀI!’Œ.Þ;»»óäÌÀó!LȌNu£¡\ÁÝy¹oÓËCʼnîå)­ËßM]rˆçÔŽmc,ÃÄ#,jÀ§slÉ^ß:‡õÖ?%ùJ9GìÿßÁèïÛX‡©ÍéÛºžYŸáœöW,2~9ñÜ]ýeµ™\1Š6P9§¼øöxþlÄNúbÃ?„ÂVE9y{tp…ÄŽ<íºhø4È• #,<ueE•T|¡£Óp~]CK⃇> ´xÊ'“Ä•š"ˆ|Ù±–¦ñ6—hÔˆˆ3¢"6“hÆ¡¼€ƒ9Âj#,çÔƒûŠ?Ë/‹Ë§VyX©<|T#/z·ZÝÛÌ®¯u†{?—•êííFvÉZQDMÛ„!ðýVrÌœ¶r%ŽQ#,fÀ³#, dÌ@z±§‡¯áá ÁÔ%».W ÔžÉ2%©ìP'¥EY²#Hg³œÊ¹Ä ®tÎë'¥$ªÆî² {ó• …;ˆNö ØÌâÅ¿6˱éßKá塼á}îÆ̺-ÝL#,1÷ÏX¹˜„±¹¿ßw÷í%˸Sœýó‚Îa1#/xÔ‰éQzÇœJ††•3•™¿@›<wû<Œû/¡*R¢,‚Õ!ADB!#/ì@£<öþ½æëì-Ë'#, +°ƒ¹o@œ‚.¡âð® ž¯ 5Ñâø“3{J0›}Iø‰ÆýçÙøÏ>8ŸŠî™½9{‡§å2¨€¾´݆ Àx Üšo5œK[‹ØlnÊ 'JEAöîÆÕ²Ú9•UÄÒ¹“œEWÃ&ß½²WñÜÖ·çlDˆ>6G6⦇™¸{?I5)üWÇâ5t¼?÷ó£öuòÏ溮À#,@ú€9m>']£SÔo6ÇñÕÙAß·Uˆ€L”ÃÍíP#,r9úGù©œt[uç#•[4éŠ Ž¬¾R#0rŠsÙV“ð(›u#,¡ÛsÇ tÝ”A#,A¿‡ÒR…»þ9¾#,ÃÃK# z3z¤>Ž?l(@,ã÷I=N›Ù"„-Îûkú[ Êw-6«¼· À<Õ<òôÁÕ÷}]ÑÜÆX3ˆO#ÐÏMo\âpµdŽ©3á–xHêªül“ðM•sî½eôóÖ*GòæzIB]}öÖOKøÁBà·˜9N©ßZí—çC˜1ó°‹B›š8Nlp#/ðºÉÏØé3‰fÍ%@#/¯Ûqñ‰I|öxH”‘äŽ+W‡Š?Óϲ®×{x{UÖÓy®ø± ¼äq$B?o™:k[˜o?±ñý2¦0Ý%ü–Äq¶1UTÄÝý-ÅÑǪxz½Û¯ïÁR©.?¸£­¶µíO3¢9_Ëóû0R(æ"1ñŠŽŽNì}LguÛ올×Ãâxwd]pÿŒNsñ1SYó­FWÆtuN¯j'N|ãmIþå '~ê˜mQp¸æš>вÛk(tÁ‰@½¾„|¯°D{6y«®–MØ*ù¼4ÁïÝ1!%rk`#s=¦/{ŠSY—C¾H¨ç‚Ð ¡îwoR3o=@äGÜh)<K#+Ú£ó¹lÌ°‹Z3Éï¼q¨ƒßeN–!þ#0`JÕKé3‹w©Ÿ×ª¯ÜºUN/ÔˆGºáØ¿„û±V;Ô÷Æhòçh;[Âß&ó¯]æð[FL1Z²mï(ÿg2¾:ï6د›çàq¨+ŠÓo.(å:mÅÔãÏ{ª·N9ü"eŸ¿b¯uu“ÕÒªŠ'¦ÄcÅTÉø?$æ±™‹•´âè»Åœó¬ôμV§TÐcÚߧ&"çñŠwpÓv3‰âû_Xß_Ã,XeNbÑ#¥Ñ#/¦õ¿#/|:l*îö7æmX½Ø¢ÑwºžžëçÍ3Td7!1¿­>šü6(?—ÝÔç„þ:ôv¸g!K߃1²Zôxç4zç–âü4,#,U#¥¹íXˆ3®¿=jꮞÊVžAÀlæX¦¹À®hnó…Úò¢}£ì;m¹§ĺ©?tì¢÷ýðÓTf$‡K¹Ø‹ëP(ïÜÖ©;ùë+Èãn Þ{mke<þ"êjtœSPô_¥ÎrÙwx4§{çé'ÛlëöÂÅ›7çÇÎý\J"æeðþoŸŠ&:¬âƒÜCÃ1#06õ´Û®7Þ¶Î;LäqºS®\"‡8SSÌ“ÆÇxÛ(2ïðwç#/?ŒÜäÅáÝ8éwwB¬whðk}fÜÁ„äø?|jÎW¶ÜfŽÍ#0´¨5sÐïü6¼™·¹8mÝù¿ËoᘇœeãšËkVräAõx*_ô¦<ŠíÌR‡´¡Å³>1ÂU!dzòÓH”BXG®-#,¯Ã3ø©`Þo°Vt>Î#0”¶ØH#/2ƒb'#09ù÷k¡Å.ÙJ8ð¼oŒèæ2Î]гڇ4Ý#/•¼Žw=ËÑ\ŠŠ ×Ç;l¥%C2I}JàHe<¿Ñ Âd¤]‘R£ž•Èá™×}Š²þQÜšÎߺ$·•<<ÊLé茾ÁMT¥Ø•3&^åÎêÓÓÃF—Éèþ˜¶>É༟­Â‘úÄ\Çß«œa½cY–1’¶Nìçú{P_m|%·û/Ê6b\û¥ýmûb»ÎN)ÊÀ=nŽ ¯+k·åo2­j%B¯h/X ¿!ÛþdHƒòM’;S û”ñ³zC@#íòôò¥_¼’-¼©í8>̘ð`»j ÓÕ¶Q£[£Ò÷H ¾ªÚãG̯Ë÷áw'èèÛ¦œ,êŘmüzD;ÔfO+0.ü>²…ïÏÔ~“éÇa”èV¥Œ*Êú†¾5<™yü/WæVe_ŠWv,ð7×ClšYwÛw¼2ù&?uÐ. dÇÙé[|ß #0H6œÔËN\«Olåh‘Ö¾OoÆ,èíÏßtœØO¥5@äž^¯Z4˜JJ`vÌ…ŸEpg4ÂèÊ‘¤ªuåO£ãˆá:ûÁkO?/Ю]µ'‘fc~?\ Ò k‰i׳çۗ—¨€/°T…êÕtÅ¢)3‡3 ñaRö°Ÿ PÞu÷><+E~TP‚–ãÝFrqu&¿/EÃl8Ë>ÜÑŒqÌgðúõVš>"Œ:ÒXyÇ™ßìé|àκTNŠ‰º Ã’ßÓ9­GÉGMõÑ:÷<ÇHgÂdþ0¥g˜Âúàp¯=m; Úœb±§Õ⦬–É׎¹ÄË>Ô'©<´¨hÁîx{Ü}møÎQHø £Ú£ã¤ÃƒpíäË{¢”¾"¤‚…‡"w²tIÅþ*ùÍ»ÇÂSÛª®‹r_d{=’!˜Žd™úÛ‚塽ºÂç¸ÃÚE7gMCd<¢p°#05ŽèM´›AÊèÂ’„ô¥²upÂitOJ‚ƒ#,ÔVÒ¤ÝÛL<}||aînj{ÎÎùV0}ŸšÜž„Z§ÓÕ.­£Ð— ÙAp¢²-ziUµÊ’DZ½0ñ@—Œ1‹TY!„!0Ì ý #,¥EΫ¡‡×):`v¤F»xmÝë’ÉÙ÷^ÚY¨y¼R©õîëƱµõÎ^ ‚«dP({ÝÑb¿—o¦aÛÓ¯m?\Χí¶x¾™T#7³×fLÅ#0¿rÑŽ°Ó¸w£ïšôÓàò§í/›+s¡PèæË?îûŸÇ}ZUÃú’í›X éahbz†8ÓÛ{[qPÞ@ ‹ÛÄýf¿¯øÈC ‰¯—öow#/#Øþ;‡ô–2ÿ)ÃÃ"È°àKq%Óö],Aç?uJ+¯í*}Àçè©YÅÚÀÿÏÝUo±ŸñY¡üçî/0 ñ]œwŒº^õnÁÑp$"°q!£†›{à+ŠØ@á¡Dð'¬gÆ÷T]$ŒjþßüCÚ-ÿ•3…+"7/ûŸü•­ý^ûþuUÓýëQË ïVI;Ó7ëW.ÁÓZÞ‰kÄìö‘ß#/Ä5pü4¸yº|¾£ùt\¿QsÜ>çâ‚ùÖ,ZC ýãYD}õø)úäòäþ2hþ'!vÑèårjñŒ#ﶿs¶¯Krpëz\ô‰½ÃÇ,¹Æ¾õsìú>Þ|¹s’’L9n^f †|ãÅo¸èx©Ó|)Ï<£ý½ÙéWõ”åX¼>¤öþ”‡ú½…±›è0!7º=x­ùp…™eú*wW_Ð{Ý©à*§÷´[¥¢Ûñ ÷!ôôzåÃWw#A@f€üW°@CÖv$ã3?¿éҒéì¨b¶]Ÿ'Þ¥(6I샭¯öû¬ï­z€Ñôí¿„ôk·Pñd |$!¾¤øBéQ‰Ñqw½ÏiŸ.¬áæôÉ2ëÄÂêçßžéyªÑpÀrÁƒî·MëÌ£Ç%‰7:n°>€¸'#0×1AÎ?g#à‰û>ýw×u»‘Ùò)É`;Ø#Žê ã^rô’þTZôG-2ïϯéôú#0‰ë íÓ™ÌìÙŒ¨INr$*TUf8κƒIÅã_îöYä¹õë¨tŽÿ‰¢9€°€,=¬¹lJ‚‡owÊñR%¦Ýxc=ÐðÛÀrNÖyacŒ2ˆ_q^å#0PZTPÙd¬¬ü NGAÙ¼jÅÈ+(-T2*kù{¤’$Hê°ÕåŽw1’·8koeßØý{Îå”­þ#0#,ôÂq7¾{ɯբJm©JÇJ'ƒ¹¸!·õ­ikß»Ò+/÷àwÒ~謻‰ô#0 yÝ)YÆ~#00’Ð#0.ž×º;’©j¥Ç¡Q¤hEKÍÅ›„ùß”ŸÓÚÛ#0dɾṺ3ö~|YA¤}«9½íPïÃpÜM3l²¸šî¤Ça™”hpßYÇUˆJßyÝ‹ž_&‚™Ñ^7xHß/º,p†“éºC˜óJš°=]ø5tžž\qìÇA˜LÅ:Ól¢„©:sìG;yëˆsÈ€¤ïÀ]åƒB@PïÁPhÄ¥#,·mH/¥¼½¦°œ¦°¹Ñ*xdñQS5æJôrTú‡¥››‡FÀüvl@öA§/_$øoÒWû^øcSóJse Žº¥*›LBég–£ÓL×X3Ü ¿|êŠ< €© ÝsäÒ{= ¦f¨õQ9¡Øø¡–yŽw®]6²jêŠú˜0€^Yéd›øú#B€gvÙ»Bä6¬‘L[ÊÉëä¶Ç ”ÙζŸ¯î[ Gp›Zä¹ÿæóxýcÍ«QOi뱕¹=}ƒ•ž*A$€Cñ¹ï†˜qH«»Ï(Á³Ÿ#/çä*µÎ§êÆLo¥±eý}‹8ÑúTJ¨‰^¤ˆÈöÈB×'2§˜¸qé)PŸMAÁñ,9Ø<ÛŸÎOÝ9ár9ã™:¶ºÅtDÝ¢Ï,nùŽyôÛ§ñG9ÈoÉrŽªÍÝ$W®ªU'I2gt©/㶑³kâœâ Ùz6,•4éœ!uf¥Æ(”°8yï{í@ñ‡—5Io|päYÃ6ß7hóˆašY—@É…Ó•Î<…r:ƒ-{}³†%lÉÃKœ,gÃ1“ÁZ¡«(™“ãöÜ{-–KsÇj;;ïÂqUñ»ÏŸwÑÀÜ [<‚iŠK3¬LAtâw¨Ñ@å#,º7µy‰kÕ—E¡˜™ØnÑ>?½F6?Ô9FAŸlP]§à†\󦨵Z#,аYkgh{:¥²qŽ¬òh4F#L§•²j=–ég*ìa²¡µ³]DÓ©¤ó„¯O¯4fÓ•†2茿clòÓßÙ‚2¸Úáû;'ªò?ÕÞIŒêI6õ¬gxŒëæžoOE+NÈ•Íù{¥Ï~þªNo8JƒãïíÒô»z§öã“rI™& Ìõç·”ÆnyË{ºqœ&ù]¿má×¥ïiSu ÍUд¼Yy‹,³-ª¨àd!#0Å—§@MIlg‰A›»ƒp7ŠÕHiò#L†˜¼[hØyÛc†Ì®³ÉsÍúGØ]œ¦[ÃÀúÌ?{w…rõNÓˆq†™H)`%\‡ñkûòp›{{ =ã‘ù(„7iô y+cÀÁI€ ÀXžž—²¿¿ÐÒ¨{,ø#/ÃkÚŒG*˜[C` ªd^ŽH²²õ#œ¤`fFïÓ[(æU¼Û—d·—Óðøã/#0)j”&hùûjcƒJNº¹ïûÞÖÚÓ¶i"@ü¾ùÞ‘øk4°ã®ß®SÖãdé*$õN:{‘Øó‚ìœv?¸„Å®&õ5“¶x÷°ëÒxH¡¡žUaø¦ Zøè2åÎY÷”léˆlÿRºi:Žyc†6zøS»Õº<›q‘“|­Hóù<Ôºg¼GC€ß]˜ az°G’ÑnC<‚ÉvJÁç×#kȽd‹TÀ{~6\¸[Jzƒ” lK\ÙQÎð°Pqõ9¤¡ÏTXŽ8H\ôϨHðŒ½«©T` M׈ â3³'’䟦®´ñy4ݦI[ êPw[#/ñ ©Ò~­%!T#,Ù ¼;}5ñ ˜I°#0ƒÊxyyŠHŽMJ0Ty$„’O,¹"#,ˆÝU£®iâѧÈ.2«2ø»ò;÷˜“:®<ÿÖSðz£Ïk!m×­þVu#L«ˆ¬£j$ӻ¿ˆºà“Nt^¦ºâ„#,8ê‘Þ,Å„g®a:;l`Dõ k\LSÕ ¶N#,Wp‡&†íñ@Ãc kÀÅr…zù»Úú¾=lëëÊX‚hêg>/M%:aÝ H¢ëáf«öãìiº¡Þë¿{©­–Gü7½:úÓé;~#0þû›í7C³ Æ^ú%ب]èç{« îZÛ›—s`“çÉ­Æà×-m¼tUÎDHϲË29à»P!ǃ©xí*bQØ_0º#0µÉÇ â-¤År”90®â)ž9©¢È±×pWå~³ ½ÍžweŸb˜ òÀ=ÕådKIóÉE.\„„nîwTk©sÚ,$T È©ôgËÉ|­,Ãn“·LÖ9~=J¸_=A[-¢šž2!r(PjPÚ¨#¸Î%10ǬbÀÀ8Šé–­ v‹oÂMsäîëã4yËÃ#0g:AhÜyÞÑH»SÌoæ¶ù ª¸#0…Ö¤ Q ´#,Éߟ& # kÕkÜö¬ ¡#0ÞŠRÒ–­€Êåv–¾¨Úy¶-vmÁœ~¡¯îâ­Œ‹Üþ_Mƒø–"½—™®?qÜêty6óSwøD5úF„¯"X¼ÂE÷‹lƒÏ`…‹â5™è7åæçA0z`§ŽTEî;Û8v7ðìãùY^–|k+Ñ{£Ô}I&ÿsÕ>Ýј‹ƒ(šKhÈPT4ctÇCûÆ5žŒÃQ—:«ÕÃ@·¤0Çuúvn·gç®SQÛuŒ%£Ñ#05|Ò;$±ÝelêXÈp­v ³ÂöG¥œ®ië‘ Öûy„Wâu΂{zG}pÔœŒP2—0ZÏ„³sàñV¨_*êpV+AAW'?]$•NÎEºNt3‘VÈù\¢Á–ïÁ0/×N~œ»}FüXú–›Äž•˜Ö~gVÜÑ›=yhhÛNfäCc××6²*Óx¸†EKåp±ÞˆèÜH—\¹ÝÉäÈ<ìŬ6ܽ#,D=HºŒˆèé}hÚÖ®ÊåYª(§—³CÚµyÙ†¬ˆ²Þ[®=Ük/}WÖ^EIŒvh5µíœf¶mhæ\[EêE·¡A[ÍË*C¢qxF!eTÙÅqD‘οv<l0h(ÉÙÛÄxoÔB&þÑåiSx±í…w_dŠKÁÈ5b¡9JC`¨ãª«ùrÛ…õâ,Cƒðƒt ®|ŸÙ‚Oo|Ù·ç¾6ãYs´Oœ;]»“ÅðëSÚ”#,&l£J×Íû-ï¹¥ “ ¸¤kÜgât®¶"2yŽ [Ž#BžóS¦·‹n¼8ÞUÕº:áœYŠ ©`Ì»”Y\ØÔž-‹'š¡µv”LF˜áZRÂoªÌ*±7 6‹5¼Hæ­;ªp:‰¤nQv½0vë¡M:!™3€¢±$Yh# ¿AA\é B½¡­é`n=¾4^þ¯9g÷¬Ozr)5±p†Á€†=È1'låM'›T·ë#,¼"ˆûøÝx™§gê»s$ÖzÀFGúýŸ_íë¿Ñvã©ÎÀášâ·ªB›©vºÅs”ñéè†èiõ.úZÊ h)¤!d2ÌþW$#0·ÔºšˆoÙÑÌË€¾‹]dúN=º]ˆ` MÏðÑ«ð÷{‡¯È=>aqÈ9årüÁÓùh£çÌ $Où,³>è.®Sà‰Uj¦­ã½FX÷¸t;¡‚©$„a¤#0ekªêdyñ%Ã"ÿM‰^ßÏëïÅ– *>³´o#,“øüUXîsÕœmâÀÂú·/&³3´e`ÏóJub¯Ëõ^^Û:yñÝølÿñâÒ¼e€Ç~À[°^`EÕìZ -g ûÅHŸÉÈÝuÇ=ááIUA‘ÉùŒÒçá›/Ê#/ê±ož˜¸ ¿™ßuÑ­#,©ÖU*è)Žã«ýLõ×ßþÀ=ø‡߀»õÍI?­è#0Ì` #,¹|ú¡¨²±ýúA#,rÿŠQT”ÿ®8à…ˆv诚üì$v ¼#°‹+pÌ8#0#,©þ‹AÇ]³ýŒþRÞ;6=½–8(]¹Û™a·û¦û&‹`ÿ…@£ò†. vƒàòêFÛÏ´Ü*h ™!©Ö܃¼ð#‰Ë˜•éæmO«ÁÁ¦óoŠxbPªgÚ7¾Â¨…~#0QƒPÔÖlÂZàòáÉl"`aà×1±íPë÷£ØAÌ÷{àúŸ’ýŽhƒ™¤=BëÊ+þp°/pwÀ»ƒàN¡3K-#› ܹüw¼/’l2¶u°, èÁ@}FÂÙ!:NÍ«’`dže=kÐ8 þÜTûTýk¡Ýþ=½Ø)½2ŠxÐ=¶¡Î¢±@¬Þòêá©È#0‘!ô—µ±@3¼€LfWœóœ›ŒË’HFóLê¯W²Zé?³Å- W#0#,*ˆg#0ÔJâ:5£êþ÷ü>ÿ_¶ËHÅOßbÛ·Y-õI#Ûçéó{Fåº õ¨c©îbæ?Žpï Rb•U×¥‹¾õ—¦$Ø}fÉ kRSy[ãý;x à|(`/¡Ë‘Ïõ’ÁŠØiâ>„7Wå_´¸;¹ë8jÕE£4±Ì—цî8$Ö‘¿«ù#/@Úka¬*p=ˆ§"|6&œÂ”b‹±#,óÛ0Ÿ½ê)#/6F”ùò’ŽÒ2~h„…ú|©ªåfÐ<åÉ8u}:€æ#,æõ'QÐÜtu¶ìPÕh ¾òLõMš£jüèn;ôöœ/iãr]È‘º–à1ÁŠ#0`ÂÖXp|Ào{ ‚êðRŒ,=>’Ù §0çs: Ù‘#J±¬u-‘4ˆhUpÁÃÄl-Ð?"Oëý!êl€^/TûkÆõpÖ’}ßX^äÿVϾ^+!”}BXþr¡€xÍS†\BêrŠ31L•ý%ï§üp?g€OÔc~Ž9Þexüñ±ïò®¯âﺣ A$2­@9¬IpîˆÇ¹æRÇý›vÞ­g^FÂÅÉèÍøoK1!Û@‡RHõpi2¦Žtµ¯÷——–¸ê·ßvÝ£ržÂ+ !) 4^*÷û÷êr#,$#0"Jé>NW¡ÔyRR°Àø|}”ÂU|mòñšŠý#±þ:Ûª‹ôŸpøÇ)O’"“B+X4o‚ H˜:LÌoä'#,„\ Ù=§· »&è?lÎjïü™ŸzþÞæYøl ­Œ‡Ÿý»¿i!Âf¹´u2’Œûr_*FfÔŠýĨ}~Ó³ºht @3ü¿ÈURò¼P¥1ýÆåA%S•.¼®{#/­ïÕ;ji@:ŠptvÈo6Ô¹r~÷‘ÌXôƬ—kçØ`2÷-<ûâ¿L9²:I#83Æ1nÉ%‚«vî’I$,K½±+÷{wå› 7—}»àvlGgµápÿõ˜ðyð5í#/«ÒÚâêFAB™oJ¶Z9<ŽL3e#/Ѻc,¥¶b)¬¢â3>ÔñŸ‰ðú¹×¾}ÅØþŸÎj@¿Ú¡Äæá#,Heþ=¼ŸõɇWm¥Uä`þ¸Ñ×·¨¸“CŸƒa(þæü†-0Z2_¹êVoä~OW¤ùð¸Úñ¬‚‡‡ã£²€~ŸFµÌä?0rF×LK,}îñõþïä õçCÆ{NË2$ÝNó"ئBË™Kdê»î½Îs«=mÎ:iŸS¸PÄ(6fÿã”4~Y/òêo-¨Š0DŒ!2Iœ\älî*--`‘CŤ#0#,¼W¸°|xíÜȉƒ¼‡¿Ì˜.DC!Ë¡£¡Z ¾¿¯ýýk󫧸ˆDªHJdW£©M¦äûqTîœPêí!#/ÒËÎKQ}P+„!ÂáÖ b*š<wµŠ+ S>ô³à\5Y#/]öçb’z’•05¾,>YÅ^Eí“%°ðXmÈÐIR¥9™%0¤Ž:Ñ’˜1èddh Mž$giÉíî+Þ±šÄ>²{& ³É¨…#5ÖçõpUô†³s‚vyñ –=¯I‘$73y3Ça“ÒºS‰u0bÈvHŽˆ}¯¿öu~ƒí~ÎÁýØï½tWzèü\ó’‚Ö´¦~œ#0Ûq;¸ýÏ8ׂßÁÃt÷Þ²o™!ŒdQDPÍñuUtfÿ‡Ñupã™Tj'û. ‚ÑèqÑ 4ÑUWB•b#0ºçîŽÍ¼fL®Zi#/dáÁÙ’H¡$ÍÐÛX¤ÅLÖ³V2jÕ—VXWuVð‰ tJÜ¥#0ÓH‚Ejš”Ô²_äÃ8ÑÆ|7½ã«,hÑ«HÍ¡­¶Øþè´G)–\}ß j * „‚§8÷d%Ęá@=ÄÒQ0Þ^çSÙûmý~gO—ºDêˆOÉß#0|kÏ™¨ï‰"% DÒ•€­D šóƒù{5tˆÄó˼ÉI~µ~ýJZ,;¼=©÷#îûÐãO¼é@–‡àC£ÕhÞ„CúÜÁ5 '1RýO!ôr×Ïä?’ü¿‰³{\àO±<·ÐµôZFk.Ù¡UÞ®Í`Á$A¥²ŠM£çL“§þ¸Ö3“BÉ[Ü\$’I6Ä0< aD8„êîe%5º&WÎf^$ aB‘:ÓÞtìmžªoËc–SØvøŸÉ9jyâ’)ü7ìÑ{Ìjç×.Ov"j‡dþà».YÌðžÐš…ï*Ÿ(sŒ‚¬¦˜½~½tènÔ¦æ‹R¢TR,$j…-óáÊÖ8|`(p2ú_iÚÏèR‚Ï_´Bôlêí ìb;ÂÅCÜ{XYûÅ’¤rà{Ü$.ŸM¹òjoÎzmÄ®úÆ÷}r{.vzŠ¢šZ¨HBC\MÉ#/Û€òš2ÝA²êÛ Pß[7uÕn€áe(³¥&'âø|MùôµÐvƒ§Ô‚ßÄ}Q8˜ý'ã9E{„ … ¢†ÆöÉŠ…§ù bôDŤà"…Â`‘ëõ…ë"#,ä¸ØMÁÜ|n­ñ#,£@]ßÀSæ@\,üuႧ&$W €è»«Æ &·ÁÞ€¸–ñ[»pþ#éáuà‚•CG½EEu|íR|ò¼ØÞ—¬,ùèTh€¢_nÐO"ÇAõàXÅûóÁóýú5>F_=èî9ÑŠñlú†ÀXaÑïÌ5¹"„ ƒøŽa›AúœN½æãìƒã¾á1§;ºÿSó:ÏCZ§(º±ý“òëu×½?x‡èõcÜ©ØEJH‘Á#,n|;a2Z5¼<lHŸ‡OUqðwž`âîhÁG Û·5¸jÎÀ;½O§©RÖŸàuq6¡7··¸ò#0#0B)b‰ôžA5ø<ý§5º°îUa‰H&¾:\èC¹9>~†÷‡ 2 RÇ̽<éI(ryCoOsUó¡óêæqüXýùû öÛä?ÃGàoˆþƒì+ìcì“=¾ûcÊc!X•Œh|Áù1»ãŽ‡wÙú¼çoÄ«én bSbµ2P,Ä$=8FI$žÐ Š§ }©wíѬV"9¶€,‘ÕæüâþDŠIõh§\p5 hiÛÌ÷Ã<ééýGoÑA‚=jgÓ¬j-Ã[[j{ür¶C܆73£nu!rwæ'x…xýžèꘔÌÖÿÊ}ΓÐPh<~ÀÍô_wÃ!B&Ý¢iUùã<’2M•õYPÍCxtÜCØtל#0næÁbq#/…ƒ™jºoyÇð99”x#/"‹·õ Áå¥!õd«"jÔDJGÊJ87"A–~¨%ÄÚªšPN65…]4%2ÿM*'j©‰IëõüÒˆžj¢1ID© 8êösæçša(Á˜Zëäm›Ð‹c{ Üô˜ó#/Ï/OJm¿'")E^ü†“,0dI‘Yëð#0 °Ù?YëOÐt—bXÔ!EÙÈæ]‡©„ªÙAkÅT‘%lÕÝЯž»Ÿ£¬ô¢xÐ&aòÉêµp;7ðœÃa|P{M§f¬;ÿ"[7d¯5U(½ÏoÉâÿ ì¯Ñèú|BݧÁÇ·)+3lb˜¥~K@B¤Ÿv ©TÐäŸâÊŠ£U”+ÿIÅ(Ô5\´¨8(©u½ä àÔ&i‹¶£.Ù*©IU¤p#/žoŸgo„ôžwÏ=­ø¢ŽÅ0S’Ñø.—ÀûÁÒ¢[ú>KÊMm†¦(þäËÚî]æ×Ý(T÷CˆÇÛx ˉÇîùÎG¸óŒªQ¤!DZŸ;ÓöÛˆ.A6ܳ®”éúƒÊ; ˆ]ss=SºK’ñ.+ó¹<ÕGÒ »ÒÔDO,¤iñÐÝÆoéä8#,±í.X,X°QÄ,&®ä/ó˜=µld»°°9™Ê=Þ<ù~¡û?,߸:|hõ³ÆÌ$³>Ô)}“x#0j\¯4Öóĸü‘‰\qœˆ#0º¨-Á#/CºOÑ0FÀ0Ð|aê°3)AЋ!›6÷C=¯rWYï[E«õ–Ûá’B#0AÂ%5J‡b‚dyü¸ª!Áƒšl5-~ƒ·#,Ô7žÌ½¨x‘±‰ßá…Ñ㑆E&AH< !ŠÃj­ ê€:oµ#/v'œ?Jvï4S#`Qñ£ad  æDbÐZ „OéG#,õ}<¾Åø¥énHõ3UŸOÆ!Å~É%#¯gêùîÕôSéÜl6B64JfQ¦úûÍ_\Ö½o#/«r3J„Ùl¶î¼ó.µ<ºçZ@dd ¿ÈoNAåóô}Y_àÞ< Ë¡w×å<Áë}½j×Æ៵˜‹ô’-zOq#,°d¯'€0ØjÄ3´Ã¬Úýº)VCôJ†Çj"˜B Š#,PL,àÜáRŽ#0CžÓ4,D çG,-d'ƒx‡š¨ºîäë¼ á'›üñ®¼« ^‚ý€´·C´h&ÍÉÕ¹?<ú褜k„nì “t=WSxè(ê#ÐõÚQ·²![‡“AÑÐŽ®YÏLì·;}vÔÈmÉã#,Úb›°¶psy­§²â=$:ºìcp•ÓU3èÁ,="|?Ê®-Ý5R•QE¿…`©ü ~ŸQñH>Òª”òóíÉSÓÖ{ðr¸„¸<=¡Ò{}2ìù©¤üDÇb6ÏèDÙó…ZZfi“ 5!xXFˆCz”z–IVI$}\û˜?f#/áÛ«ZÐ0ÒÖ©‰ƒ2¥¡ãd täBGÔoâ|8ÑH|XÚG¦ŽŒR:G,qqBùtþáç:uÕH*àš`„"Ÿ (|HžŸgåÑÔ^L$'#,ìßµgaÞ†(Dh0ëéÛÓ†?}w„5ËGÈ!ùæ/_/r¾¾Û 2ãJx¶1®‘÷ï…hŽaßH¬Œb‚’Žäô°Â%´ USN½t±L‹Hhq9ŸaŒ¿a÷çÙ_p6¬Fÿíôcø²¡[Ò‘ýuþÌs˜¨•?Íò>}Là쇣)®D7ó©¦J”ƒæRªÑTzNÏ{ÄDÚI&²74ûÔV‹§Õ)óˆWçC¹A=¾Œ—f(¬`¸FE'!ö°£™o*˜×põ c%Ìl¤$Ì/æ ñ Aõ¸uÀöHQáÞù»s¶?H=Ï¡V‘JX,‚ƒ…äp]±á™º \yœÕÉ<€ ÇòýÁc²á²}¸rÝÚk¼ä#,;Mâö“ƒ€©l×èû,—F¦#0 –át|ºŽ8©Ì1};t•9‡0é#/ïl2qÝ$…”[‡é²›#,±ÑàV)znn²rÀb"§N#,açÄ:JåEXw™Š}(,ížÕFø©·É×ðq…gÃj¨Q6q=8d‡x5Qc!<Ok‹¿WïöcS%oF÷¶'È»lÏk6ÖWüŸÚ™Ö¿~N'¹!ñÁUÊ›*Ê#0 Ù[_fvÅY$ÐTNPK¬3o=¸Ô‰ ‰Î{Q#0Ø›—õNÇø‡8œ#V`¿=;µQŒÀõ‚*dýri(®Sòª--lm,L»XWW#/òû;„sª*ã°wö³àeÁ}¢Ò˜xãâç>>¸¦†/gµ$UÞtWJüeÌŽ9¼Z£™èåÞ#4šÒêx’€W¼ í.=8óâê‰ÓP0²8%w[z„ôÓý}¸Ô’L7mB½JØxï#/ÆåÏó콸ZdŽš„^@ä¨gCò"¦Á3;W‘½¸Ò0!2#/^6‚)a€ÂJG‘¾ôWß èCÁä¡×Ùæ?Z#0½AùöhÐ؉6I[J­‡‚ø{x8¡ìÔë4Fv”Êæ~sКàf`ðT}nˆQºu'Žvîà©”7£#,ò.Q'´tú¡á#ÇÜl‘`¢—/l$ï»wCnA&Ø ‘>ïœìóù»ûL/\ìÿ‚)BÝ6>Óžß_ù™‹ø­¦[é<weÄ/®œößt lo tòûœø÷ »F—èÚS'”¯O¢ äé‹CFÖ Îh¿Ì“éÛC?/ºÏ¨ùe£&¦d³ž6Þ Ùçÿ'öÙ§gX\hã7‹ÎCwÐåS;ÿ‡¶ÚýlÃ7<][ÓøÅ€n¼†í•/NP¨0€ £—ee5hPl;0:Ô‡5ÇBàLˆnºœ}ˆà…˜oZ ð$i–h1à๡ !`#ÐB)Có»¸À«Ü¼Ù€~?µ#/¢Œ~ØŒŽ\"H±‚Èîé{ƒ¨ ª/hmKÍåÚ˜÷9eê-BƒO1Òpvôg“APÛòüš·Ncdô…<‡òÇ ã(œbØè0µÊ³:SƒþY˜“ñÔj[ŠÑìCÐYV-KÞà\Žì:·ñ#0/@zƒaL!; 3n\- ¾Ÿ?¥öB3ÛoÝl<Zñ±j»çÕH‚6ù˺íç\Cn°¬‘ #/¯Ï…4Ìϱæödš„±Ö÷6Þw¦ùÒçQ]Á wc?ÇQœ¸G3‘ÃZÅà¹J!âØÑ)’úÿ?Ìîå­1ü Tº4G4ò…­¶ˆCiv½_eYňrI%›îÙ¼ ,b±"‚ÃC¶\ûºåË;´½SÏ/",Ñ_Å[zn€u{<ÞôCÏ+²ÖMè|ùûe¡Þ¯¡.@]UúRÁÃÜÓÖ|ÜøœA”#,j}ŒØ¾þ¿mÅàxCvý MÃöùèèïÔ¨HIµI!\ü¯ÀvדÔéƒud-Ÿë·Qþ³³s²Ne–Ú3àˆe[j"¨¢£ccuÄBI^_‰T”Aý#/s¼¦·ö§ö80¬c8i/ãÐÐ,ZÓw“óí\{jnsÖ‚b#/ahÑüúÏÐqÜ­µF“`ÕëÕæ®é_ÒfRßg[Ú¦CRHÌÓâ_Ÿõûñ±ÀîWŸ?#0„¦ƒ´@ºžÑ…?%¸ü ‰º{#/”J¦uÑÏ&­…5ç@ZI¹Êr ¹,ä}/kÔéù:áŠP _TŽþÿÔ˜˜¿8ôÀ>Ã|5Ï»¶ôÊ=usèz`u}ý;'ÝDŸ¦0ûžð[ž&=ßËß³¢^tUÿnãØSY†y ¨ áøü<°÷3wòïó!fª¦ú϶äßÑA„`íì³´© •'îé¡ŒÑLŒ  i*4OQeIÓÚT,Pau,‡}XvcBœuýÖ×û!Ôlj» 9ýž)EšÔù~'ã§_?Ôo‰Y"&ÕÙ¬sZÈË°‘m$#ç´öÎçÁ#/ÁhA Ë#0<6ßtWrVN¯â½ÉcöS2d‰€óy€r+Ž`=¢RëÕÐ\½ƒú+€—¿ÌåF÷¤ËõfQ„¯Ýó°VÓóàQyDJ>è…¡ƒsS7œë¦e²yLï<­ÛÒËF¬2¬'Ü2dA`¤À!Q2Ýa55|Ž„ÌsC‰ÉÒ¡¨QX'B#/L-%LÅ,+*y§ýA6§öÏ‹43R£ÿ@Ìþ!² *kf:ñ£þú±F÷hpˆÍd¤m*¬3XEé–µ#/wb³Ž0\&šµkpoFÌU÷ 8 ;B1ØËPoº ”ȳ‰^¥ +IV#‚ä %…bž˜1ÞÃ%’ħAý²þïA?µ5öÏå±tdÉ0#,‡³+â+5¯%Ÿ"~;#,«ô5G‘Üìð¡&ÿÙ&+ðÏðrȉ†ÌßÙþgtèþ 3÷3—MŸ“i7D|7¾»èsámÕ¸8„AêâH#/#0KE’ÒZEšqjvuì²Ó¹Gcü×Îøx·ÙþÒBNs‚Á÷zx€—ø|ÍüîÃÏ´kj#ø^gÏÜðœ#/Á͹qÀ*Âë´µ¹=´˜É@š^Ed˜¢¢ð9ÍiüØý'Ÿ¡Ø:éòéÞüÄñMïa³™Ä­3jXu‡S©ùÍ}%Ì~³JÏeÝÿÝgì ¥Rð#, œ0¼*5#,¡ÛôÍLjL¦É$”‚°Sðàç$FÕy;‰çñE¯CÞà¢KÕÓXy”2…º÷ëU4Ü;à‰Œ¾¦]YêÙÎ`ð4ÍÉe•€æÉ(3ÜŠ0R<¼j…] ’üŸÎìÁòëæw$!¡Í·þƒ¨¤\²L¹pzï·™MGÍ8ð)4xû:›{AŽ®#Üm55~E÷¦3uœ‚MíªÊ0Ïlÿ3ÃS¡ŸØÛ¯Ô©1}–òZ&¶±ûa61Ã9Ôí¨,áá@!Ë#/ì¥Òù‰¨¯ØOaqrkà{zÆÒáeÈ(tŠ]ÍAzÙ0»'[îæÿb]çi²÷=Ÿç<‘Þk#l¤ÍdƒÝß[øÉì3EÃ5‚C° ë…Ívå­…óNÍúˆ^Nt{ÑRá‚zÝE÷xçÝó;Îs°•ÀB`CÛ »ý?Ù×€øIåæT@Ï…/ã7R“õýVéùõ6?íïª(¿}ΘôBU8ÀÛOã¸w¾wÍö`÷¼¢øG:<?"¯Cê»T‰>ó%Y"±Ÿ°Çm˜èšÛç:G¿åö†þg{©ÎâEm¥Ê¡U—œ5ùtH}׆8²ñ…ÂÇCèf«äã{cm€Ç“°é‚¯ýqa‡ø?é‚çÀy\j¢Žäï‚òÌ6ŽÇàiXÙ1¯F¶‡>ys8€õà8#0#/Ô®ˆƒ[;Æ1Ž¼¸Ü««Úë|#/â:Þ"y‘Æ5—Ñ ]/â[ñOÞßÊBŽûîa1K”MßAG[ø¯“ÛÀZ\¹\±xx…Œ¡Ì–¼4wÇdô‹•ÿ‚ÉCÞ 2áQή¬Ö"øNZp‘mÚšiSål3µÒ–¦Åé UrXz×g8Ä:Ds¾ŸYÚfúïpãÕÛÙe¾ö†;ù»xåï&ï9¬+cM¬)Õ$#/;ëÂ&s‚…( ¢²5§\åºá`”ôF˜òžu.­n™¥1¤Í”ËÙÌx~Æ"ðoû£á“l.äí#,ëM˜à¥ðãhöÅÅöy®ZçE¦Êç…òlÏ‹ÖãPµj½RéAžÙˆµÊ#/ïñáÙ#½wÓË/yWã#/éyòíǤÞN˜»˜†á4.ŽæîÛ,OdS®ä.™Þ:d3µ¶¥å·³ŽŽ'­Óƒ[j¡Éj† @AbŒLÑ Ô¤Û5%E¾Þ¾7éqÛ“Œœ+8eu¨•G.ì·$„ÙAßöÖÝ#0¤[ïܨ%¬†òÒâ·¼ N‹ …’Ïþ3#,H€DvÝßëóõý’÷Y¹F"¡^®¬ÊöðØý•AyyŽ…p—Sµ0‹CúñM+„±¨Õ LALHé|¯“nù·Ôþsl~ëÏ›Y¨ß2FQ¾Ñ?~p¤¨/Û œ²ºab46BYá ×GML„®säX½¹ ëž©÷;ß»©ç9\££³ÏhŒM<JSçjBM·&F!@šÞ@9#>µË̶Û@mgÙ H¯DØ ¢: tµ3l¸]WÎû"#/n_¿Ãô¼“:ò>E(ŒP(Í>F¾– Oú0P>Dùþ¤çê…•£¿otÄZ%êå¶àz÷Ùºǜ‹Ï߯È#FÕÉ!µyX<„CèìÃ2o#ö½7ÁÔÆ|>J;ã#þÈ‚æ€ã™ ¼wcK1æg,’üýÉïf hêÀKFÀïó}µ×6x{v#/Øqé[ëßfŠ’ø9ÇBjQ â§fßÉä9?~ýö콌z"ö71·\MÎq:[Z}TS‡ ñM“^¦~œN†#0à ,$ µnåÓ{fdÆ'9N\KÓu.Δ$í@:hCëÚ c¨ôy¬þäÓ“mu‚]ÃÏv˜¨㇉^6ÁɶoåkÖ%òý‘»]Æ˦yÓÎ%°¬›Râ&ć}7<ìå–Ìó@¤[\L¬MDOŒá«>¬FajÉ*vñãQHŽ4Iz©#Coa¶Ê9%»ˆ”‡AX/ÁË@È6iˆ±–³¼s4®`Í1Âr£!Š„†_~#0>#Ðø±…‹Y‡qæ"’2Æú|ÿíƒæŸoßãÕç?…íÖrc¹P+ÝýßL/±ÿSÊ\êØ¡•ñTçsù¾ïGQÃLÌÉÈ€T>âó¶+í÷[©Ç8²"dÊžªOͯð÷\þ¿Ú>Göÿ?ò>a¸‡÷ °só¥àã|#0òªª°ÿaç`]L øÿ¼acUÛ@oØ붉ĭÛÉ׉ÌÐ6ó?#/»‡ ÍÔ)Ý“úB‹·7¼J#M!%"F#,Daýï$þm Žðã¤Úèu*Ž¼v’Ikp%hlu:Ü¡„Á°Áf³Õb6I.CÑy‡³1G¶I`È´›–òg¾ÂêH4|?Aܪì<“n!P!t·Úÿ°ààÊ÷==ãÈ5YßÝŽÏF(;±\Q£Ì’=ˆ(ÈÄO0£Ìþƒc¼áü#/€½"*¬`ôªVª•F¨¦ÂΤÔ; †Õ;Ͷ6#,Ðc®þl4Û¶„âz¿…?O×]Ä…8%›@Ã-î;gáåbyé3(æö9å#0ýjªæbÃÓØh{ƒ;¶>ç¶xIZž†›RVÌcŠ½Q6AÉØlò!ÛMY¢“U8²GYÕùpwaƒc#/ 1¬1ÄÐŒF"ª4aF Ì4Xpl¾À2ŽV4\ã Ý„}!œB °%–Å^v¾F“Û½Ü@^[@Ñž^ŒÎœU%@åËmg›´…ÀRÜÁ˜˜ŽtÜ6„ï5*Ø¡@'ɳÆýú³}ä…:suqjçZå÷ü÷:[õ„aæÏ¢#@ßïÎî¿5B¼³WÙ°€uñ¨X¡ùD“Îå$›¡UP´2hÉS!‰«¼½×º÷Ï6¼SjK´`D7¬”^׆ë…‚ƒðaõ#0Eî[‰+W÷Ýq#ÊþóúÑø·Ä¥e,Š/äólkeu›º"ŠÊ•D²0~ÒêžÐ¥}#0r4’!WvÝŸD$ï;ƒ#{ûÙ#,ži“CÏžþ‰  M ˜rÿ'a¥¿¶ÃŸÍÓ¡ª¬V"#ˬ’räXõòAl‡¥È±˜ýd68š†u$cm"`ŠÃ•‘Â¥ëÅm«#,=\Rñ.¿º»{™LkÐg#,ÈŽç)wI†n!Æ*x6ý#/‚‡‘È Ú¨ü!^ªÎ0ì÷&ÚN©¬3(Yo']©ª†Oy#00f# ‰?aSäi#/`™PÆ Ç·Çu·{ŸËC×n:0Ü€‰©x$"Pìní·`¹öœ#¶#0·ÕÙ£\#/¾ê/VÛÀò^ÃTD£<ÊÔ_¬ó^EÚë4è*Êɱ?›nãzXUZ÷TÔé„2ÂË&ò.úK!wdZ#,8›ž)s›¹0™ÉIAwxQ"ì»9gƒ”Ò5W¹!Œ¦0’¹&Ã'›ê9àÑÐœ³1ŸL/sÄ;ü"«Ÿ¦+µ\e89dxdŽ<g-)ÔêùëÕë¯G<Žm¼µf¦I6Ï+Þ¸|ÝAÊÜV[:ï31"1Am©ƒâná‘ ³D¦Òônœh]Xm¥XQ#/^ŽÛx<ÃIË56ljØBˆÈÈ7?µ‘Ê™í^ÞPÀY8'PèjƒçC>%×­ÈÕ…„ÎF'Aw!CßüµMöú½ští™Dë¤Å,düp#,IIÑ$4ßÓÖ]úmëàHC]¨XDD˜2D;O©˜± êã±îñѵũwцösoE(¢ ´ï ã4Á“ƒ±r2à .¦3¤$(¯5š¼Èd1œLæ´êl±ŠZìh<@°Þ‚%—¯ŒvlLxxkÎ@ ŽçG=# =¡npHk¼/†‰6Gœpë+Jœ£éˆÔ:°®¦ä¹¾Ê>Z&èõ#,9!Ä>?Aäôù‚Äš¡°ÕæA()Ølt&$tneøºî\ÌÈ´ÀôÛhl`ÀùŒÚÃE„ öIy·˜o:tñ6ri=3ù›×¹øÔØ(Ë·³¶¦àædU{&G4À£NÄ…IYXª…Gc¦âêG `ág¼î&a—¨²tˆ%Jæ‡83+Mª.³@8éû*¢º¨ÜS¥ëpSi6›±&¥ÏÕ¹$¢ÂܸÞMÉ-]#/ÉMBÁÀ„*P‡jzʺJ™¹Áœª§Ülhiı”æ-þ øl¦n|tfca;A¦ïy÷MË—èksJ€ÁJBØšç[Öö´ÌraU[r™iuºDò0PcK€õ/‰#å ÉÈyI#/G¸ (D`«,<ÑÚ3—‘() °5šV8/X÷<'%0åQ©¸zŽ7”Ú!¸_e±¹W;‘2±“4˜‚£IÅ…Z©“™G™ž°å¢êt9ÑÜ`5Ô5ê÷xk2˜Ëb¯3.<ÌÉ—332ÆÙ™Y™3$•c¶g’^Rýú­¶kÕ­¡HXfï±—9b¨jŠ $zHØÚWΈ–#/bÓöùoÒjÊógtv!è˜Éåôe»‹sÛáÐ…¶9OÏ:ª5õÃV0i¡ŒyÌ…Gnñ´OÆχI­xxãK-×EÆÑ9­ÐÐ4Β蟆°kdÌ÷ªeù¢AdL!#/Lá­ôæV·<Ö»5xa=L™;vBP sÜŒ÷¦#/‡´^~‡Þ1Zšm6“#r1YT’UAD¼ :i0÷õ>n7¢×j x±2xp­HÔ¢ÂA‹ˆX³3¤–ľœÒ1­›eŽœHøÝ-6ùžÖyÕS`U,i–Wp†YŒÖU£ˆ˜†Ñ…– hˆ‚óÒu(,Kþ°Qs¤ãª**Ñá^brÚÕGƒãºæ]Ÿì¿i°#/€…IÞê7Fž²ü|£‘i¦ÒCKHÍbîŒ@i#,Pp·”¸BdÑb#0 }µÖ'‰]0’P:Wq,bLt#0á8M‰Eë–棿•Ê¹_›|&´(±Û`æ‡6°ŒtJ›"™½jY²n˜ßmÖo#/—#/ÓrJmÄJlðskDØÒp5‚Ð6“FÁhhëÖÇ6Ç–\¹ ±aïàGºjÆÞáDö›„Üà£J(‹Ðã$á2fdçqªºDÒ{pM¡$em½Ü`Ú½žÌ6¡… ÎÄý;Ï›q¨2„d¢4Øuw&Ppd†Hc’VÚSIÐèªL‹¢ðÎ`¦tGgƒÓƒGYn„e™ @h6m€¼šî×D p­6'ÿ¾7 qRV..Ä8âjƒ 28Ó†A•ˆãŽ‘¨ÊöCý/Ѻd~§&™òº¹¸K¦ûF(ÆÂl‡p Ðå•s ¥îl¿;_#0«UÐ !„&#/ö ¥/6î&{ˆŽ™º&)°àr}ú˜ƒ\`R$ÍÀløˆ„x£*rChƒ$¾êsÆ1dT\³¸e|¤a¬Öɸj)±¾XV9g>#0†#/+d\¤¦«dÆV#¼]™Îo€gÉ7&»Ó6ínR\00Sˆfsö6®÷D¤ÕĸíN=Œñ½'wÆ©ý—PZqÀíÜ—ß“À ‰"È(Á‘€1Š#4&¦¢0A NÒà{NòdÙQÂfÅgA’'‹À1=§:äS_?L–·Tg#g,Ð7‘ê)­í1£«Æfe#/™è,NÊ:Cpo ݈p(UÀ¹QtLŒõ)¸¾îP{X¿> æý(RŸ3hàkÚ˜ ×ãׇK»rWÇÝÄ.\‰¢Š¡ˆ­ÞÞç³Õ³Bº¸†±Ñ®9yV†Ô—‰_ÃÙY&áÄÄÄÌË&a0†#AŒz†s^ë 6n,«$›fimÛq*ç,P~ΔnÈûŒ“?ðhtþ  Pxø¹Å‰ b|~¢g-c±F‘’}Œæœó½.í oä”"¦Ýl\F‹-g‹1›5*P›^ÑÆ^EЂÝûH¢¿åR¾ßü¨ŸKÖïþâ?ã ÑŒ‰¸“¬;1ž·Åq^§«Ó¹?ë>Ô--OÃæã[È›œ0×·³=Pz ¡i({;<6†‡ >üN‚üÜYêÁ†PdBa0$…‹ 6Ce)Õ¨ø !D(Ûþ™¼;eQCP§¨[Y#0 #0°eA >çDpÈ5Ž@¤vìs=?ÈGýHÔ~Vëÿ\ªHš#0Eláöð(@'ÎRˆªŒ“ÛóH#,>Ðp1)#AŒXBŒ¤ße}~]ÃÚ<éI#/~ÿ9Aÿm›‘}g4sæcâtPu)-Ki’©H˜–½î¢##,`ÆDX©#07ÈÔÎhÐ2€Ub]…°‚ƒô{k&)ªÅ±Yø)r<]^~¯—iÝbݵڊœbÎÆHJYç ¯AG}”?Q÷)ý2D»R† #,Òˆ÷kJ€<‡{] wXåaî{ìÕçûÅqÎÒH‰¿¤<Á#0gH¥T5T“óö«,0bØ–"‹h®"˜  ðÌóB(HOº}òå4Ê ©`DÍ]ŠEP7)¼9˜€w"‡¦Ú1€ˆÀaÁPX…ýºÈ d“DË>ƒ=”k$$KZŠ]ÕÈ+m‘EO«vö×Õv¯UˆÈ'+ËØ\šÄÊ ¦)˜ÿÈ£t)£P¸Å` £l:Øy#,€¡DD×i œø %9V°ï‹_6Uuø—ÑH‘š #,=GPK(?m>˜I“žâ C¯q)Ð@2–1ËK+ 1“¼(Õôå]å\îǤP·.<rÓù_0DQÓ ÁÁ< `.{®tQ‹ÄA]Ð* bH£é2Ä0UzáU_Ì[UV-UFµšZ¶â!‰?¢#0#8˜ò~€ÀÏÐ’zmÊÜ- ŒÁQ£÷8ÉG¾ª FÚZ¡d§æ#0ÊÛ\¼R[­@`ÖšF#/`8#,ØÒÈãuô(òT¦£?Ó5ì‹ÅzjRI÷»xõ­Êömì¹s0×wi$¼»¹r¹®?üóo7HçGussIY]Ö9$úçY/9¾u¯KCéR§ûsâ@˜5" p¯ò„b*ÁXÈ0„‘,XCàõ¯h€‘PöÌP¸mý#/À(Ç? ÄDPV=CØù"{1'«Ôÿ£À­²¨¨®xìi¨Q*5‰Ã‰×s`wö´ÅŒ¶’>|Ü/ƒð&ºen@¥ƒ¯"ìe†XŽLõT0’Ï„Zô#0I`ø¼,DÒÙa¯üß"×Á>\&›¿Âž- ,@#,‘S8‰NØJÄPAƒ DŽß“CD™ãÄz}·ä%»#,Î%+ûJ¦GsVk+Ï*ù–ôfjdõwûRMGƒ%“‹Jp!LyW8Â&̆Mupö%$É #”h‰CKhgº•7¹˜ÒÂ’ã`,˜._•Š® HcÕQÚ›ø·Rï ;êÃÕGð\·ê#0­ý–Céþ3žÝ)è§è8Bk\†S\ý>fÖêOa÷xÄüÁa·q°‡ÛV«k¾öÁ .€½Á~²u輪k…ù.Ç5~ˆ¢{ß¼íÏ—ÇIµ´©åáv‡ëŽB¹2Ûˆ:Ÿö!ݶlj¸|ñ©ò¯-·É‚Ö gPgóÌÄ°í³’Ìý)ßh6Äæ²¢úOÂ#0ü}?*! U*¥}¼÷'3½e(&ÄóÒgKâÜQçAA !˜£ì’ ô 3!dE/FO˜ê©³L:²‹ªUâ[#,:mÐQŒ…†ÈVŸU•"ƒî¿ž}š\»LÍÆÎV,Aw†ìwVÐîÏáí:ÊþøÙeT¨ÂF¨¦I]ð&àïêJ¢Ÿ„nÛ›V-lÚ4ik%J$¶+›JÕ˜Ólš•±Tš¬4¨Ð¦f­HTˆ*yNèœmò•'(pÔWÀMñRDD‚##"$ˆ/G´dFȾXR#/€@&g{üûA^…¿†®,v¸dRƒªÇUÌ2`<42ÅQÎ$ÌŒhXBÓ1¸?¦ Ú#0²ê†ñT1X“XÈÔá¤7×\øD1í­ùñH«DCX%ìbÙ¢mâi´sw†<Xnhi(È°QùÕ†dŘd)J»*Ž¢®šP+Î#,è#0îŽÍ2Œ?Ñ}ÇÖH4·Å­ƒuÅHòŒ1`½2›zïpáÕàð/€ç( ¢T¦Ð]6î¦2„ !–Ð ï§Q_µc›5,¶üsTi)š­Í¹²W-vi›e¤ÁQQE´VåÄ6ÆÞÜ¡ñ•|d‰ág ”7Æ2"°$‚È9*=¢  …´àùt­¯ û=¸[…4ѶZC „M}è¯GF…„ô=CyŽá:Äí»SóÁÚHx|!€ä{ä=wwü~ÿ«÷þ¿›ý_ÛÇ‹Ì“æ{qB! ð!gw™~#/ç½{.iÜÀ«–½jü-Ó[_“éB0cñ4B!E0*!…_5S™ç6á{î;|ùXÅ\ÜšD´#,Ö”]`?”šýyYE>–/° sŒ¬WñÀè›ð Im»¹«¸ta•Hw¥øÿU§ou{oŸaý å$üÔï`i7V ¨eý½Ã_•¡hzùåáÃ1ÈðNÆÝä)Ì1#/·„—ƒ…8/£ ÃPÔ÷X ’§Þ¶IÉf@iœ3Ǥá5Ð6@D@hÒ:#4›z^IÒ|:ŽŽú;Ž»ŠXŽs€†(Ǹ®Òv•a½™ß©c÷éA”ýòe€;\F^þ§G§Ò{7¿„'xC±©ß›sƒê¡ßl(÷Ý°ý=‡X(iòpø¼8Vç«ì˪¢ðxì–¤aòwȇ°øÓ§„ÉXæÞ]ú9eåÏÙ]ôâCcØôØòÓ07Ý¿Ì—*üÿÍ'"¾à#,ÿ\Auëê’0$¦€{-¸PÀ1f ï;µ*Œ¦Jgâ”.(O¶W7¬9õdÖö' -¾Chy•¦©8U*“Ñ ÈgÅtÝÙŒâê˜!Ÿ#"# iä=Ò&8·ÄÆР⢶î±aýHxa#,`ö•Hø‡à ÚûA\òCÝJy !¯O#¢²·Ë÷þÿw¤¹W”þkáà2 g•TT*츈7D«ª>®‡ˆüÅ"0_8UQ*„€°F#0,ïù¿ŽPÌå„5*+Ç$ ÕÔž~]yC#0qÊ‚Äol­«ö$<òAV1óš¾–•€Ø8xÈ‹{wپ̅ƒqs›#,÷s0äœ(EbóYʈ@Ï8|ì™:?¯Ÿ¢»c/RüL¤ Æ̓ìÅ¢ šLÛ®~ãÔ¡ÕÁá™Câ*C¶×®Ë Jê:•ô†^œ1º´Æ)a¸\·+Çd¿®ü|›¶“{Vç2= ne>|ÏEã|sæðNQ‡›a…6³U()8¬jÅs¢#0Û´síx˜v$U¥ƒˆ;Ö°:<=v¶ã§jìºxã¬v×<®}ä¾ÙäÇBËÝöiu¶J„Ø@—(íÃíŸç­t ãyéµ¥G^=ݦ,ì»(É»Yé}E׿<{c*[#/Ï8æHˆ9x–ÁÅôÜëg^M³µ\9æ>;ú»‚­›ý1lj­ß>ø#i"0 cµnÊ6–8ê8é0Ò§j-C4ÂH#Ô(¼X¬ÌÙ¡·ŒVÍMÒê<ÊÄ0(ô4|>œØué¼úî1TFÞM˱48X5í­Mºvô\˳$7w4å9xQB#Û×€ž‚xü_J&>sà|}iSñsØp—RˆÞ˜U!QJWç¸]h~}§>dÚªDp3G€¥_¡w?pÄÅ ;Æ—€/s €#8ë#/}×ïdd°{]Yáp0áàBB×#/œ0]¶|¬‡çNˆž‹æ9Ýþ7236›pî7Ëä“>úñ® Z._>6ÛÑÝIÖa@_”"ÍÞ~!€šãÖg~ÇÏí|ÄÈ67n½ Í€ô½¢ƒnlCr#T™,¶À¹ód½™òKN6C†¼+ÑÁVøG=t¹P¹¢tðä9®ÓSîuì  Ëcžç!QÄwg#04p‘ׂ-UJ ŒF½|N[b ŒK3E/ÆïÂêÕ(䲩TõÖ'·ÙòÔô”PŽýšXp0 @joÚÝÈêÃá\!ƒ ±L‰Ž1‚¯Ò[!då×®Lf›ùõÏyŽ›45ÀûX—avxN¿6œy#/…#0‚‰ýÝ €­zªK”,^çñ`ÀèÔŒÈDƒÇ*¥huI!õ4{ꦤoÙåìÈó3Ô'#¦ût¾K¨ý8˜õN– /Ù›¶Ë'z)š¬©©Å?w§]ªÓåÃWVåç•D‚ùƒª^ÌZÙ¹@Ñ9Ýß@󢱘7¦ú¼¯/;«M6¦¶º×1K#0²ZºmÙ;«©f‰£m#/ ÂÆ)„„YA ®XŠÆÔ,U§l¨!¡J"(†—SËܱbDp\FÊͅ÷\pº]Rá"°H\&Ó8&yAH+$PcÀ‘$¯á;ÞŸyr(Ú›9u#à±dÏ(7]¢Ä軑åmJÛ¦Ť‚„RH"ü]t¤Y€m%ùUÓfMfKäBÉ„$žûÏu•@D¤…ptœ tß?ÝGÊﹺdê®ìÈ©Y–ËÉŠQžTê °¤“vÖ Ó%šy¤ÑíJ·vÚÑH§„ƒ#0×`„ã©jÛ Ão©E=_ªtÂ8ë¶ûÖ½¨#/4»v ™Al¯Ù…\ éÚyD´Åìªý.ÃÏ—=8í<v‰ÜÁ9!>Xò;å|Ôè« wJÀmeP#0aäJ"ˆR"±”ŒŠ#NFâȆ"ZÉHAl¥ –…Ÿv’¹Ð¥&`ž:Ÿ*³Æ`…ð+]{_†û¦V4[ºíVæ«•¢†!ò(¶l2—SFàaiõäÓÙÄs^\òHLwð:󯪰´½)³ä³ö·U^,›-ºâÛ=‚È„…ôx2 Zí›p$µßžAæ‰ÝnL´´Çiª.5mõžá¸i#/ÁŒ$ž{vºk9äòÿ=dËáâYt¼îáØ­´NÂ0ª¨5! ±JXòˆó Ù"â]-dÙHÅÌ3B¤q Üɹ¡Vù‚—‰øWâÔÌ1­“ °ªd…ϳ»áçð:ûã#0’Ý°ÛTzO—ocÏeW˜#/ý;ä£kz(W#/¾|r6I4M¬XŠ ;„E“™ç„éUçkWÊåÕS9¦…ÊßN<ÅQ\t˾Ó.ù2pIä4á‡Ì&÷F>Ãs)Ë…É$œå­Rצãßgµ9zVÜNBÝФƒ Cîn(Þ»o=_I¶Þ@ûyœ»—8ƒw”$}ù+HØ_šk`#/N`œ;>8’P¸sáÏ|¦±d7ýºõÖ:ÃC°ÈrÁÏÕA aUX¾aôëêi®Ú¼#,ã]‘kpÐBm.Á²‹Ç‰!hl.¡#`×ç• bHÂbæiÃÀ1 AH#”8"#s¹,Ô#/FAè@P€×EiЗ9xT Y"q››Ý”¯ÙŸ³<Ò ‰ª±#üØÁ#,ÂLÝB¥E@¨ŠÔ]%d€¦Àx0ZÔ à2Ѧ ÆØ‘`ŒBˆFµ€W#/#/0dL0Pqg™¥­ÂyD»°¼pÆHRÒ_BAãipÖé}<¼ÞÓouku#I¨†RE`MÊ#0$…YF£ ]JXaé²Ý„6ýÛ—·'¥¯='ªpJûiLx2HSÜɸœ#/ab:›õ|`*1qîQ|™FÝœlTM`_KXÓ7ÐlŠéc#,Ú§¬(¢äá«Ÿ)ô1AŸIäÃ&¡IH£:m°{û:àG©Áø’_¼–²_¸4q×Y7?9¤9#,³É% ߊÁ²«¡OÓêÓ0°Éf9ÛÙááêíGÒ. ßÉàfiÁ,œ/á]6öt¬DQ§.¦Ä©;–?gO9ƒ¤x‡#00Zoªë´ ºµºßyùÿ%þ–ƒlT˜:)Òªt©Zžï/§c¾ûCvg̵ëRÇ®tO¯LYßé6àO#0=}3r¥z "riqJ\Tc" ú b“­ì½ëó¯‚óÛ™ÌV£Îÿ¾'Ɔ’S Õ…Â1$1#/Œ2Æ#0'à”ªEaŠ¨f8bÂí]eÆÅP*˜Æ†$´Q€Q¶)zh†-Õ.»ûìõ2…ÅbT«aÙ€—ˆg˜#±#½àsvs:ù7‹ÌbÕ ªö«xæV‡‚ÄnÚ}#0­[U¡1à#¾† P®wzAú#/uƒ›ôÅÃA#,p&9éc`º`Äfaú#/Àn#0†è¾³#,#,ÂwýmUM…&º¨#,â<&;.«V›T³ s=λ&¾Ë™¸³¾Ž§-²n±Þ¾‘ÈÚØm¬£^2Y–d˜•ûòmŒUÈ’ ¼1fsä€îô¾ŸÂû}õ >)š(ô·­èˆÿ$<ñS<#0îˆeß{½Ï+1ª 4Í]6Ú²‘6;#9#09V#/6)¤þ›1ôj2¢‘RAÒ‡ÛÛg†qÁÃ7C¦z5¶¼µ”® ¯>¥ f¢Q•z‘2t¡,j´¿s#$ˆ7j0bhJ,„@YPb`¤1šÌo@¾Ì}Ó‚€late<€µªT=êÆƘ,GwR6º9bKÍ¢WÖkŽGž+øï4ÖîvtËu b¸S Õ õݸ%…nFÏUO­Ð”¢V¬Î|³–Û÷6Û\¿JyR Ußb —Ž7‰Ê:çlRëÈãBBU%;ÊÏ t.š8þ–¿¥®C8„ñóèˆäÚņ˜†ÍhKÅ,Wq6Øi‰ H¢S‰Q"!pª¡£}.ËÄ´ß,¸JÊ9ðQA¤FÀo€œ1l®¾¦”1%G¹‘M±2%Ë;I²•œ[Âh“t5wB ŒEÀÁrÅFЄS@û0ïFÊHfÍí±Ì.šäq€ô2&HD3c %¡‘#0a,VMPË{Ô¨Å"Ã&Éá`T²ÈfWøÁòMåµC?äsƒ|LÁ™iG+)¡$^·ÌfÈÖÙiW¶ ™Â¨'0ò‚1­t3»ñäwѦsÕ˜%Ò¹üèU80<B’`åt$%u“Ž…Ê"U³˜–c™@ƒÁµõºf4Øq¹ÌeVõ®7óåAÉf‰‘‰Ab#,Ƚ0M„Š;|(#,×¼¥54("qïJM=4)ƶˌÚy™ÍÿGÇeAÕÆ>©†ÿŸ2ùĺVõ2ÐL¢*[¼0 vNéxXß²uÝ]0’m·ÇR«cÕs³d†Mé×)†—Ÿ*Ïq–£/Ê‘pÔÏ—“ï|8ìl £à~dæ´O/±ìß–Ú¥nem[£RaÝÁÃL¶ÈÀ|™í#±¾IéZ‚\‹‰,#/G^À› |¡ïGñ^‚‹Ð†:þ|cÌW‡>>v °#0@Ïî5÷ùúIÚ–¦–Ù°±ÖÑC .”]°õ‘`ç‚Г÷{)d^õ€ :,ÃK*ŒI¨’&&›:d’9€•„LŠ­€Œ\lb±¾Å°ir€4#,ÞxsL:mDX5À‚4áA„#,„ h›ƒLTS#/Û97Gé"Ð^l#0Ïã’4‡¯ ýoE‚¨’tÿ{,ÆoÆîËù¡ÚŠ#, ¦Úž:•í¦×•žhþ<›¦{“Ùª”ÞKÃy{)‡9#sÑ&1¦ú¿¥AØÓ„ß¡€¾qmcQÓ?i¢š—•‹sšÏ“©þ¿\Añd‚0Áè|¬€ó%²§œ„>ÖEQ@H,–ýUÏÜI`m¢ÕFµâ¤V¢²H‚j(È¢’Ñ…#/©ñÐ{<ݦs"!†Ä](oDð_IÎô#,6¥” T‡ÖÈ×ÍØw·:šžãé:Á<EŒT#0ˆ)“)fÔÍ2JdÓ›bÒeAûN¤ÌÕ¤0bÓJ”2SbP¤Ö˜}½¹C&´˜²J%3k4)“)™#2#04"¨¦%)aöwQÆÈ¡H’)&K(#/Q™0Z*Th°E0Š3ɃZb›bÈL¢h2f`”¨ÌA¥ ˜¢¦ŠFùù}ûîUvvìwR#â›hºÈ=ÏÆb'¡YG_¯óí¡°Ÿ¬¨‡‘¼!hÕ#P¨ä_‘ÃÊb«"Ã3Ò£§,d0u8’[@–õï86¦,PÑé,?Ï(`"°ßS·Àà{л͢ˆH5÷Ðy?}MÛ™» …­ÜnWÅÈÁg-Ú½w5ÎÐ’¬L ZS÷K^†^¸¡9õÌÆÌŸ´–.xø…öܼ8iÐmm†8`a[XÇH’Xl#/þHUîÛð×ê,LjQ¤†ÊÉX²QŠM¦Y€HÉhs0w¼¨·…R÷paD1šÊi÷4ãŸY‡23áâ«,><ÅpŒRS$¯–9m5RE2-œ»õÀ¿Ó\6·Ê^ÇH¢òz>"‘þÇÚCº‘–¹'§ièêO¡Þ¿‰!ã<Ùì9† —#0¤:j‹ ®¾GpbÐg+Ïð³rê›Ó.¼Ì©¹jK˜»q>-ö#×™²ˆBNË#/ lªF©H´H#0’2$æ0êÞ>E#,”ç 6ª½nʵUyu¹ÕÏ9Ä8…›RuŠ®ïÇDÄb;Æå·ßÍçl;ôw‡b¥–w³ãÏùØ„ ’1,-“P½QA#,îñ®ÛI½¨~öA‡³ËÆžMx¸3ŸTöE7>sË3ì8qÒó\^ßfžúméÇ¥×™r5†7äßiƒ¯K Óì˜sÄxßz9Á#/¹ý—±¬È°](%Ñe ã-tÖÙáYÖ²ƒ×+S*,¦å¤žOA¡7MDSŠ ‹Ìy%Ñ ð’mÄÚÂðk©FSW/ÊT§©òß5É{áÄé(Vôã„&Wá ‘•¤”?÷B`ì#/ó'#0ª.ÐÊx JOKdƒmw±Ñve_Z:o mȵ¡ðÛIj<ŽÃ¥³#,OÏé›ì ~žšO6Þ}ÍÙÝЯA­$P!ËwÐN²­óÑÐOMz;áÕ×cðô–0Ngq½ÿAÎÒt¤*He‡ݽCîòÈC¨ìÓëçñÞe©ÛOÏð ²´÷(–#bP¡Ø'-¸ÉæË©Ã/Ì´ñ*OoW…½ h•K/AQ%5˜zSCoá²ÚàUÙ¢¤¯£RÇh ¸AQÍÙÈÆÎ\6&D#/3ÉIÌŽ×q݃Ý<|‰ßPÌt0µv“—³Nˆ®€€Á °Jì ,ÀNeûÐdF6Ö½gÀwéPIZ¯[3#/å‹,C‡&™ò}Ñ‘Š<kR8df`Û3R°Óƒæ­Xåy$cz™šÔiû²Õ½Wô²<3l»áÌÌr^uqŠ¹¸e†ÙÈØíx,à âhÑ©¦U…—‡3KYªV¡¥¬30¶™:ä™…6µ3LÐiì»Íé«#0°ãôL͹ uämánò˜Žƒ†oLuó‚ý·]¸#,aÇݦù Á31š£óu¾!Z”ä‰bȺÖbèMlÌÆ´lÇX#/¬kMRÙëÔ¯ÃÁ»¼#1²ÞC´ô}N©²·EN Â/N8¸qÜw—©x3´aq±£Õí`‘™ÆÊ£LÀ±i¡'"êÁsIÊ–²Íuèb}î©Ñ~ÑdäL(ìeIo0@˜©~¢+CÖ†'wgm?f™7†ÕØÈ:óm¦¬Ü,èš«}FÊrF­Ô”Á:by7fë²4ˆ¸XêÌ…~¡¦&#/5ríægcªíµxÉ:ß\Y—Ó¨#O1ñÆvÆøƒ¸uÛfJ3­FñœäÒÇÏiùÌRa‡Yir}Ü›ÜÓñßn¼nÉøõʑ°v–vˆôëQ¥«¬Ž°X›e£u)ÔáSEdÉ6“QXÄ9çþ±Ú¹½o¨;íD<Æç:q^q8‰2Ëœíƒ/dRÖ/:¨ÖLâñŠêù©ßU°¤’g·f…‡m¶ÂªÜßlÞøŠ=5œg-÷ Ûbä‘Åž·’tõx’Ö4äNѨÇ7ÒY,mxHˆYëÄÇVÃeØâª*…?•m‡ÆøyG:»oX˜wy·É:}‡¸4ÛÌbŒÇ}Üm³»b4Pûb+ŠxzÝž#/Õ—¥)T³Êdl„²MLc•£U4™¡I„„ÖI7;-s•‘â vÀnØÅ´…3:fg¤#»B ÖR®–#/iDÎ#L‚Z×I’%ëô‚]XoŠ¨0(ô4ˆÕG-CšnÇâ †]Qå’zs¬;:ÌÎ|iHÈVôø‚Ý5qW‚°rûE옴Øs(Hd„„˳«‚#/™ê7ãÉáq¨w åâî>‰å—INuª48ßxuÁ¥æi[ÊIwcAyÊÇDÉàÍ&æ`¬½F:ÈU ‘ÂãyÞëº)’ånmellðqƒ(•Ã¸„rŽS¹Å?³NK!ÉMyêmTs†@S³Z!°Ï$>ÎЈȒ\ŽØ7è$h'ŒÊuÖ#/P\Ü@;ÂaÙ#0›zµw6U;"áÖº< ؼ‡e½åQxáÎaèHâfX'0û9LC£Zyè›$ÃÁµ;b˜ZsD†Ï¦—†Æ‡—®Z9ËÖr›bå©4Ê#0‹€É#¤¤¸êéƒÈ.¼ÁÔ×ja5_r›ãlÃ%tÜÌ,&–2#mÛíVî™ âÕQ#3›"@ìHn¯Tc‡{síT\À®#0œ#0ÖN%ãð§ŠÐã1M:I"æRЪÏÕoE2¸v¼nSÞàœ«uh”’xœMÑ›DUufîýzVK¬ÞW}J‹“(JÆÍX\'s¨ºÃ‹62ï,Kݸ͖j¡…`šåØt¥/K»#0µP¬«s&\Øõªcp}4vGC÷™5%oZo4crÙY©iW7¯«ãÏ\ï8w+ŽJ‚\¸y‚fTÄ[Õàã[>ƒ4RÂA欭E'}ªò|EáÑN׆*0¯Ê.i NÓÒŠnéKjh–ÅP0 ÄÜ*µMY©€Ë-M¸Ü·Ì´+£ÑIÃ$8ã57V#o‡§ŽRL©²±³8º×CX÷§#-š»Ê马ycÉUc2ÄòL ÝZ™­×]í–(Û2pÊ-4ÙYa» ¹òº¹³áTqÁz¬êÕoz‡Š6C\¸õŸ\ÄkÄ›­b³½,¥šŒ)‡ÙžÇ/˜"ÖnL“ÆJ&ŸH0ç{–LŽ›Qo’D¶WÄPgGÈÌF'êD^eKÔÀ䩲GB)À1A&¦jr°4ƒ´6Õi.§8Tqª2ÉΦ-R¾Ö¥Œ ÙÑJ÷Œy^3xôÆÏVrkOƒ'²qý,]—Xtm¹ËkI&Wãp«Ñ$Ò±6*ä:‹wœÅÕ#0’%;“¢æÍ6Q}†Nhµ”oº”¯KÇÇ#,ÒÛnºCG3G v^†’ØË!sYNM#0Ó#/°t·E0›$!²ø` ¸àÙHŽ´lbñ8c‡²!‹¸±NÝqß-’k?‚ŠÈ¬=¾šu ; D“Ðÿl-¾”R(E&1RšIYÕ…b£3ˆ=˜‰i‡ ^¿804z0§ƒÁãL™`¹6 ¶H°ÜiТÉHmT°PÇZË0`aBËk5lÙ…<šHŒ2Ì lWìÝ%²Q]¤¤‹5ÅM“ÍÈ™ÉA9¥ïStÂCF^•)#/Ð2É¢KG+„ÙLÊ]·ÀO–õÉ};?#ƒcS+‘úÁ…Ö^ºàRðYR-1¤ “4i98&@]L Î63‡¨Ï<˔ͮlpqAe2L ^žÄs´‹;‡<ðû¤dË펄ü¡á÷]Î’ÜJãMηieÆXÌë¡Œ¾Ï|ó[!ÍŽrÒ±-ÅÈ}#/Ô6ðChÈúM6éñ:²a%f” ã}¨Ùøïµ¾»éÊÈw°Ø.¤² °<+ 4ÔbeÁ Læêi£0ÙÖ3Œgq©Ç@Xzòa!‰­¸Å”iµ¶V º ʨªß±ê¹)Êd.ƒÑE³†V0…“–še6€¥Áâ3ŒñõÉPºvHŒÅ·¾ŠÆø\Ñ\«Æ«ÙµsFÞ¶ÁÆ‹`ØughƒÉŠµßA;O —c‘’T48Ô4m ¬ˆ¦IÃéögX[.UŽ±¥¬—üÅÁõi¤ÇÈÀ©ùo3 ©Œvî­¸D z$b_xá³ËÁ´8Ž¨ ¡↨lá-ˆ€Q™Ã6`Š¡Ç}äY2C¨Åf&—¬Gcy#,ÕV%ébí [Ê¥"!À74o¸Âc0“Âêor ”:î):C]MŠa|‘‡š(‚\¸'i©P±BàC'7#}p× %Æ‘)iN‚™PLÂP]¹SD ²e¸Ã%½ C0é#/ „(à›™›pÁ­T¡k@¹ ,¬×›z‹“h*ì°á6@ȸìÃ\ÁÌn`èÂÁ¢R9ˆ•‰c}í,OQÞÆa¶jCjpàÜÈ`4™‰v[HÎp¡RX)Ü:¡À¨>n쩵“1“5fª¿›-v´~­¥/úI"B¶"†Èh&öZ"mÔ¥0ŠzÒ ƒ_ëÙþ#,rWìñ{ˆí_e~M·êÝ®Š±‘ZX2¨q€b0IÖª ë€0¡3l–¥+õÑ#0’ *gP4MŒi1­æ€#/$°  .ÀH;dA~¿OR›C»:~¸X&|©Û*†XgÒƒãÎñ9l‘ñí81ü‘dsæŽVÅ>›¶¢DÐLÍio¸} ¶ Iõqxxn+¬F·²3qRÓ͹™0 ]&MfÒ´##0Öí³`‚Ù°@Àq³r‘ZœC^'`bÌ·«7B,èìÆúõ#,c=¤å±Á?ÔNéj^ƒ|&).P¤IŒw©Äÿv´ˆ.ÞÛ·N4v¹fk_&µ¤Þ@„B*ŒB†À¼<Ñ$€`°tùY zºS.ñyæPg@•¨-#0£LÐ4M(–™ù~_:$mpý< 7CÇêúB#0+·îÚ$H_Á£³+I•²’HˆšlD$M.nº\Xn—ö+ÍOê©îE­œ#0Þl<ð;TO7Pò'Æâaþ˜d7I)›AS€ ½€ñ?F¢ô#,Â1Žtƒ˜ûˆ¼xõXdâE«PQ€Ž_qì¤Z»J2ô–4?.2b]üê2.)þ™¹ÉúÛ ìÄÛýçÃàÃÃÓ‘ˆc=8dcœr¬›ôØ&šTšm乶/;µßSÏ4m®þY4º„²…ä|r ÍÁiM_fÁ®Xâ8Rh5S† £…NðlxkI›Ž)…ÊÒ“:E̸ÝLðýÓËfIE­ %Ø4w‘Ž@ÈE6Ó_/ˆX©jÚ –FòK™Aì£çª(„ZÆØ£Q¶¨ÖÆ0›¦eIPhÚÅ„¯¯è‚H…(ÜxÞ'Å7û O6•¢§ÓÜ(%ºÔ÷mëð\SÅx—GaÝvÓgx'´‰% ¾è±n6ˆ,–"$Dm0Á%à ’j;Ht¶Á‹p#/³#,ÉÀ<'‰ÙÇG@‚@‰ØôÑi<]¤þk²x˜gƒ´ æNiŠ²™C'éº-(©÷hU“#0ý#,î×xˆZN^<œ«!Y†IÓUÍA¬™d¸8ó©AoöœT€Æ,u¡Ežâqžµ«•t­õ­ì½[ÑfVñ¢ì˜Å³)K 6 i€WV— ÍŒXÀdsùÎ2‘5Ä´AX‡9½ð‹öÎŽÑIbi¡ã‚mÉ$ ÛIƒ#,Ÿ8eëk#/d{†f>Òz½·Æ)C<L‚ov¨·š^Bg›Ñ&Fݸâp6—.ÐÛ Ôq”/yJtÔÞŠV»:6Ó”@R'qpŠÔ^&Ž²BBüÀšÚÍ@8cÉœEÆ.5•"cH1¤6 cq‚éɈ®Í/Ó<~pr@<SÂ'iÝâSr1‰&Æ2°%"L)ˆ†Í0ÇÐI‚â]iü"ÚÈ!PXKÀáglrAƒLÎ),1ÇÛ#,3^^UV©iãås3\ë—SœÙªg 8‘µŒ¤ýr…ƒ2•÷T#/¦­@Óz›\ÍgSRÖ”Â5ÇÒ‡ÖóŸyJ6´a(:u}KÌí6ˆâðÊˆïŸ Jk#/Ùh°Ði‹7joEK… ¤ke1`ÖÑm-,í!-ŸëØ1aàsÓ‚%u^‰gUŒ÷8yׂ–œÙ`æ³2ÑX–1$¨V ±Z¹ª$§‚»º#ÌX+tûš²Ðea1„F?Ÿ)ÃÁúºÙœ¦7ì`‡wgFÏb †° À呇‘8ix7œ01•‚œÃb"3‘2´r²äMªžæ#0íK7†õÎwW#/pƒ4 ÏñqÖ=‘5:ÚmuË;Vôƒ]aÀb¶t‚%—.F0(£ݺº°Ä2±Ì°Ð¸#0,J‰UA*$›iA±€š€ÇHp…ˆ€@U,$˜;Ʀœne '¼Ù|ÐÄVú‚Þ(yöð#0—p{þ”ú¿ø³&}Êå4â‘E‰ì `õà­ÒƒÀøJÃæ?ìñIúaïTAÂZ»ìžw°\1£»@zdCï‰V«ÎzSBQªy‹ÇìdŠ&w]ÝÜ»–í÷ö¼WóÕQkFÖJ«­&­Wæ¶T´i%ù÷äÃí0}0|ö´ñOtçÅab\½ðŸTT¡ëí?¢;ê—ÙÅñ;âY,,Õ¯ñƒo4Nº§xê´ok™Laʱ+‘CdÞΚpÖw™Ù4Ì™W52:2¥Kºye¬Êu…X¦Êl!²‚‡ÌÄ#0¥«; ŠéJ"hd6Ä0Y¬a Âò—³ÎfÜšááH Š9-Ö<Ì8à q©„¡•¨zyv§‚·étµÉ'ÒegypÔ›"&æK¡Àt)¢øš€v"ÞÄ£h†P–ŒJ†±W#/g¥sùÞi¶?z"#0ÞM©Ö⼉“¹‹x²Ónœ¤R–ŠSæ)#/ô3¡˜é:âLJ¸vFµPó:}¢Þ*¨i½ðxXÛz›Mp“\ÎJùš}2”æ¾Ò÷dÆtÙq’L•„3ä]s¸Ð&QI E ‡/J¥òtl›PÖÔ9¢ó3÷0°| P°]‚Œ#"h;‹Ûi©õ÷Ôó‹µcUvVU-Ê»EŒDqX-–QëËØiZe¥ˆ²-%!Ђ¸–…!q“]†V¼¼è£]n[iÖí¬ÓKkšœ’èR¥*8ÅŒ#,öÃ¥Yº’Ã#/óþš¨çÞ†˜eP1šÕ}1ò—uÛ[´.uŽÍÓlwuz“su2î:ë®'N¼¢kt¦›!·Z»I¬•Ývîâ“E¯;¥-;Ow]æZñR¸Í·+vš£H³)ÿ ñR$ppRŠm5 `4’·Œ¦´Ëd”™eclZš‘L×Sk¥ie-2kJeFmTiö__žQ«3A€Ñ³6µ¡*+h‘$„Þ[×ädò5p¶„Z= ‡S☗ IˆvXh"ð#,/yÁ´`¦ J"Rª×Tyf4§/š ¼ˆyÎã×`ôe„ôŒ;^½?C#0XwrØe;t#/#/}†ïža»£h'¼ˆàî;ø½Ñ~\?aWêG/ñìÞz†Ú=šòÒÆÀÔÃà]#,"Fƒo“2TÔ±‡›U¦ÚqÍ1Tïnÿ3[œÙaÀqoRja@œGë [Á—¤I¯å‰Z1! ÂåÓxÄ32¤*!h$Xpàr£ñsXïÈ©#/X™»3=ÚX·×“™ÆÚ~0#/šD°*V9Sdvf$¸Tƒã\-je2+á¹@À@ŒWM›ý_¹×–Nn¬;Hð#/MØp2Ð>uz.<ºtu?¹“˜ü¤òÁ½öà¼,Ñz"å#,@¬\q¼'T?w…<EÖP-ñDì€!“#,M˜¥®^–¨ù9†Ä%Ú°g¸>Å šŽ ‚낲œ Øk1!#/¥Š¯î±€U°Üñ5·Ól-Oºª§ q)r œšoîä¯ë#,€H€@ˆ/‡£äBºaõs67ˆ› Tîµ”~Q€ôã:”pùÿÐÇçab¦…•X@/Ï6Ö¼KÞ®›cj""‹hÓ4J“ÞÝTD’Mã[\Ðf¢VIŠÞn«\šm6‰Ki6b©4Ë&šd¥c1K5 Leø›¬3jÊd6jbKEj–Õ4^÷m-²»ºZ„ûÕ»kµ|vìQS‚fUi’ZšV¤«jhÅM·ñ–Ûªû…F’l‚¬lšÖÛ$‘6Ëm¯•Ú–ªI©kyÞ¤–›M”fTÕ†Õ–Ê߶Û-±ˆÑ-Ñ´Ùo:ºó¹%-S+•Õš¤Þ ¬·uFLKm ­xMqŠÍµ¦ÿB$+Ø¥á±Éò›v†4ɺsÕCþó&¨מ§Ûôž-{wðáЊñ7ªÈVl+†¶I„6é×ëÝâ5±ùýÞΪµX™¤ê¡=)-rœä¸rÅFÊhÍ­o›m>{¦jZZYš[JA€p²éõÑ$’h’"¡Y¥l¥[á¶Û¦­~fÓKÍÚå5so³º×”ªéQTȵDdAJß@­„€š‡J¢gMtÚP¢b¤E4#QdJ´¶j6b–32Z¾ëJèÂQ©IjSmZm”ËY6°Òš)R›}kr†ŒI-F³lKA1J4¥£Ci¶RšDÔ™¢Êeš-ˆÂŒ­”RÆÉ‘,#06J²UJV#0Š”ÙRQ*“j)6ÈbÉeTjͦ•!BJLX)%&L“L”RÍTÛÕU‘#j)bel˜E•­K5“M)’Ò–Ùf´Šd€•B Š$Rbˆi-ô•ªé³fµJU´–Ú¢ÕòÕæÍQmæj±khªRØ/…UÍmH•jTÛD@ÐVh»²p^wõ ˆuœóx³ 8¶½°†ZäïÙ2í›0ñÐ#,CBTîØ?Ë8ã(š9ʲHˆž#/ÛBÏi%Iè<Á¡åâÒœ \àyvTµD‡Ë+÷ˆ¬Çh(û¢yþÜ0D7ÇXõBC îÐÐ(ºŒWçååá»6¶íÃÍ«k(&cÒ]ÛM —C’”nÒ‡ƒÈ"¥+´Äº¯’Z÷/.Üÿ+€Ñ‡¿7×½™C¥#0ý¦ÃE‘@Y q¡„†ò<€§1Ìdq¡1‘5,Ubª3C> Ñí#,õÈ×ò°O$ tPXX äl5ÙºŽw­Cl«<y5È?VJØ¢)oÏàP‚TñÏT8G~D0 ‹Œ¥–VŸ÷“ï~fÅ)Š˜=Q(¡ÙÇÍ–7mòCÏJ°•C|v&™x‡Xœƒ^[±Û}Pîðsø/qX‡:>ù)grãX°þ/—<¹{ô.×B¤Ì0(¾Š‡ÉRÀ`ûèÑ-iúå[¨c¥ öíg#/‹ ¶ðJÎpnä)'›QE«Æ䇖ü#/±ê¸7Tq4ošÍ`™!³#/”TІ†eíKçw”#av‘ëX…üŽ—Öù÷mñÚpb¤]À#,±¶ÆªŒ)'#0¢€¤WJå œÉÐMÚTAöƒHÐý¾¨.¡æu´ž¹=Y)*`" ñ}9ô>h " dØñ)G³Q¦Ý!p: ëèa®Ñ=ï»éHÙmzV§Ðfoƒ ”üz5Ûí#,òÌ8—Mþ/wbèpê󺯭ÀóWW|6Ä0:Ìoì#/¢ó #,äÛÊÖs®™À[§øþç#0«UuÝt¨Š+›‘~Ǿy'ð<øvæ½X)WtÑçwË~ûzæøI2ÉBJ1­Ë#†b‚£Y-ÈÀ¸&*«¸XàPDGFØ€ÚƒN$âÆŸÓ OM:â¬A«„I‡9hå‘`Û¶ÒB3› í{•rÞ/ûÞKÞq„Þ5çŠ%UdVº]!0`©jÐðH°Â¸ûè)#/ñJ·è©5b0k ±Òôï £Âg+Ѥ£Á$}ê›leÒб™èÈì#,q#/cÞ†ýõA™ˆa+BcMTL,UŠ¸Xí¹ô@M°HbІ}±çÏV¦8)tú¯6“°WdÍ‚×gj“‡&½rÉ/Óá5),©F`‚Ø’a€¾™¡ÕÒ_vÀþ­ªÚØÎéb$=àüþMÁ‹:hƒábç ß·.« :¾_Hu‡Ï:Ë©"ÊcöM(pü& Åâ*ø>iB—+®4ÕS|òó›B½6Ô[ÅÓ#/ …ˆ£€“›4ÞjËÁ-'Á™óØÅ©I*"Êß^+ÈþªÇ<ø}ø{÷1á3zðé÷=1è÷ß)§³«ˆ?®L«Ïâe9š³ò‰.­'`-1o¬K+@ýÁ‚nõôã~ă–"aT’KJI>¦ËÅ0#ó!{¾éµlMi‘¦”‚&¢Ê#/#/4’v$,@ð’´`ÓTpÚ=ºˆ¬††á5BÇ ¨Á“(£Ñç{A´Aƒ×:ö}™›,;il™¤aPª °kºxDr,×l£ÌÖªÀ`ÚppÄÏF½5ƒ¯¦§&h†<+áÜÃò¢¨H 4'd#)ú)4jÞÏ Œ|˜@ª‚'SÑÄ0è¦Õ,•T½G$°NO¼P)ã6åç3Ú¦|ÈB`0B"ðl«¨[ÙB¬SIDÐã¤Åõ 2æcåÝÈߧŸ·µißôög)í[£CõÕIÏš<<Ó‡¤À0¤ ‚„1ð4‘DÒ$zÑa\ž¾*ÄÖ-?gÂó¥´ñi…¡Ï~k5£Qº™4Cé¡«a4¤Rbw^ÚaA¨mw{2´K»éÞÃmeEÔˆÙNÈ™Últ÷e±a°6›ä°j&@È0èþ5$åÍUŠúh¥^Qz‹#,ò==Ç îë7•³<Õ =ä1SÑGßS™ÐTX²…„¦1€‘A‚k¡Áµv„#´#/4µT²à€Ç,fDh¥7-çËûÀ]ÛI9£¼^ƒiJtK#0HТqAß©b È©#$ʪ1›Ýn»î—¾mu®ï£œ¸ø´á†7½ÇÍw&MçuÝÞæø4ø÷‘¼Õ>­÷5&7›"lQEU"¨ª¨lÅ OMj%°4JH©¥S®¤Ù˜b…9·gѾ|×{Ü’„LÝÚuÂBŽ<ëÁÕصÉM.yçZÇŒþ{‘$òº»”ªqÈ7–m«ƒs"ˆcLgYgîˆÓ-LSHÆÐÚ¿ZDV¯‰…&/GÉë]ìô¼9Ij0<P_"*C»b».a–åì{De¶Ãv=ӑßeg¨é½„å´?}RêTvÇ=ׄ¸Ñ^7Ü<2¶ÌŽNDŽé%‹¸‹Åôu¦ÕÁ44‚pÛæôeÔ%T —Ä=—ÜNi;ž‘MAéÞÓZã! õb‰¼õa”cå–q[€F"„»ŽŽ¤æQL#/#0¤ÅSYUŒ'oš0ƒi4{}*¡ƒ.'èÒ¹õ<,UIiïmEÝ×dÛð®È™”HNóÊøÕ{«©vÖÑÕ•$0ŠH¤/jŠ9«Ò†ºº™*åÌRøL¼Ê¯4Ýyjam–Y@21@xðí®&&`ÀëIJ›zòØ»8<fCæúöñÎ  ÅCÁ#/#/,_O„çØÎåK5Âwc"Zë@Ä'Ïs%_¯=^ÙfAfÎëj(MnBÕúZÓ£F¬‹–ÿC#,°VÚ“#56BL (MY1B(¢µW#6Ë4šl®º.T3ºé¥E¶6ß‘¶äEˆ­¯iF¶õz½›Zñ¹ZY·÷_Íï²V´Û*¶—©.¾%Ã.Ía;_»æ…™O·XLºífq!” H,€¥ÆP 2Ó‰Œ1Ï,X1O„ R9<ïp¹‚ÓÐbã! ˆþ·Ú¢ë\ãÁE±‘A#0‰>„øqçÈÙÆÎ:.XGÕ½–#0ÁÞn37œG×% ÄOñˆ#¿`´¥¬9o¢¡é$6Øj¨ü•J£-U=oal Ì1)$À«Ø3ŠæH<ö8}ÞTbŸ«mU5æÓMw]±i‘¨ÒV6ʼnJ @ °ÝëÝ+Äs0F¸HÙBòѬp0½…Ü2囑î¨p8m芀Dâ†VQ-åolÞzBmÚMò€Z#0’Ái)"'2Å¥<ƒ$ü[x c,¼0úÿéà+€‹µÏm&¡€m P¿y· J1#,¢€”®&€ßöÛq7õÙö[ºXÌ<ñâ¨Ë#/ôQBN"A@'äí4÷k²&&’‚_G߉æOŽ“ìËu­ýÎPR=¡:™Ò@@ÎQ+#’ pf-Ï\Û÷4'šè°å#œexóS¼+ÁEZArO׿ÖG **§!ªÀâC­¯%v•]'.pñtQñµ=ˆ‘o· ¯àÁ1!ý:'3U2ß#0jŠlAåáâ2ÇÞ¯Ñ@zý& q¿8‡˜‚"AX­¯Å5m;µ¹«jém¶,, TT¨ª¤€„2Gz6,À¼¢„‹ ÁdE¦¡èÇ8×æDè[o÷¡ƒmú˜3%t`¬Q`fD(£ ïÒ:LNÓšs>«§¬Ç÷mZ©Â¤MòRHô$›“Ξ´£G¤'$âlG•œJÇúDáÌ:$’‚’q vÙÚ[!Üaµ%ªE–‘\4¯!$1î;¾ŽÛ<Ô™¬Ò¨ÄtÓÓç¿!¡f‚^‡)e‡¬Ð /Áäz‚íó¥µî=uÎ6Ù·¼ #¬h Ú¯qve¦#0BŸŸeã”?‚Æâ*f, §5Hÿ=fw¯WJ‚ J~¸U V ½H§õáŒ?sZhÞßë/Ù­L1Ò¤X¨ªP3û®ÜSEC(y*˜”°–; aY‘€RŠ1dQÖ¿ágîf„âÓb˜A¤³–.ö 6±Šv£®Xt°KV,s|pa‘d#/˜ÁIx(”˜@¦mÇ{Ñu¡CD)’[ pBÑB<)iŠ¸¬Òc¤ÕYb5{ײ¤½ñÕ‹yÍÜøzãFÒ¨Z‚)"²iÙR‚’+h¥ “F˜-ëA„1.©fjC<(†bEjH2&´X@ E˜Rã…(d†˜‹!g4á‚ŒŒá† ™LŽŒ´µ‚ÙAiÔ£tʹ¬ÒåædU…aŒ9aZ,M꡽dXeƒ‘iªÎ²cM*1±´Ú£Ž2-‹•¢›»b湨¬k˜“óç½^™Ýv·ÔÈ[¤®DFq&tZg#/Zu‚­Â$¥ÔoüúÌ4Ë o‡á‘Í@¦²£¬ƒz}È®4MQIi¡tZ¥)ÆTª„#0X«+ìTz§­#/³ˆ H«%‰w o}ØÐXeÚz#,2aÔ”d qüF´>@}4¶ÖO*“ ×bnt!cì| ò¶éŸ¾ÝªflÒE¬šÌ%¥"•ji,PÄ i‰tb}þŸÌ”9âù¡Ï#0Ü{:>(y²rDàˆ@TÐ*‚zCWöÓ>è4BÄ"‘«¢/H‘Zúÿ ß»+ZwžVÉ©³5UvÛ)Q&Mã\–Æ+\­·R±kÛnnÖÉi¥M|îšËYv«Å"hn¢¬@E‰Ø|ÓøÜ@§è&pTRÆ„BÀÿfã´û®E‡Oà‚$@ƒÂYóçÝ×ìïÆÓЛ,`nqú½FkŠžm$H*ÑT£?t(-«AUE#0Dœè°KƒMœ'MY44yóçö}œwÍ sÓIí}?ÏÞùyÍ6@ùMÌOôDÆ#/à^«–Š+oÜîø»v·­\._SÍ6EAµ‹+Q*+h' b^»Q±ªMƒZÃd6ØéXØ®«»b¯Z¾.÷È^õ»*ß[mv–ÑW›ºŠ,ÄÂ’kRV©3M’øùíá–×Ȫø`‡bIU±”YqÑjV?N]Fêh£ûÊpŸD¢ã}K&qVAe‚¿*FU&`=î 0™I˜6$Ë!2ÀYc*ÂÐ÷ÔTkÓ,œ`óqØý’–n!lPü¡³Æc£/FÕ‡±IÂ{Û$6H—}ÆG}:—ƒámèÏ/¨úø±ea5j©8#XÓ¿ßîèmëÚ‘}(œŽ ¨V;UL\.ÜöÐUH.#/‡Žüøž´}½ŒIPPøi , ­ð ú¹DÞà5sê#,J¡³‡Ná]@ì<U{ʼ׎uvÝÅ%SiJ˜ÖÑj½›v¾“]£Mk4ÖSâWQ´‰š+)"[s ¾N¾½(Ow·„…¦é¹4Ç9oŤH@ÂUÀÐfzöwØßPõzvΑ8B1Gä?îó°ýgµ͸˜—6m-A¤}ÑJÆ&ºÜÛzfs»?d«Âeçž4]o”DÞ¬†ûç}2'Mª÷’$F EÙh¢ ãx¸ Œš%ÈV²ž#,—,8…l¼IT6ÌGï]þÁðò{zÄXD’$“1: #0?¦71q=^o 7 0”9¼Îž©C@¨ g½)á È–! IÑŽ0H•!!Jˆ`¿¿Ý´6æp’lèè&þhoS<0gQÒUɳšb?uŽ6 ¬Dðãu©QÒÖ•EU+Oì4º]0«,Ù†„Œ×rbßFÚîm„b!‰ýŒ©&Å0Ù#/9$¹Š„s»#,ji0ÒÅHThže#,>¼uý˜g» Òz3C„3q]HeŠ>Ž(q±é–9k£_øÂÁ2÷eŒ0Ë™ú¿ƒ3 &NF¶¥ÚæÄ:¢\ëoi™ËfYžÒŒ¬eR|᱓«¨¥= ê}©¦kÑ\6¨}Í'B¶60BVZbØ‹e¤¤¤Ù¤Ö¦Öš¤ØÖŒŒfšü²ºM*-,ýÖÚæªfU6chÔc4ͤš³jbX­•l­6²”Õ“kÄZÅ¥lÚ™K-©¢J¥cVØÚHÄI¤Y™øÞj?*aýïÒÒqÝ€k-†i™¢ âªtÀ8~Ô¢ !«k©ÊÕFÔmQk©m«•»7ƒo0¨ÁoåjZõ­y±œ#,Ö='wW Ä8(.Ú5ˆÔB¥MÛ”c%Ô¿VÓ|Vߦ°›#,€qI?YŠ¹’|9P"‹&…Qɤ‡Å¢1”ú( 0üôûôè|Z'¿{ÊÊtÁp(ëÜ]´Rñ!YóU¿O/ÖçБWŒV@#0 5}q^àÌÓpž¤ä…Ÿâb9µû &#0"ôÅ-D¨5îr —µ-ÒãBú™â³•â [G¡àO~Xÿn{MÛv\ ½€Ž$S½”ÈõÚ“Y*Rša°[#0dÁÒÍÜQE¿¿ËÈÃ56?¥šdbãã½þ¥SÎDì#^F ‘|Qæu#/®Ó¢US`Ê&iï5.ï^OP;È;ˆ#,˜O›q,1„X&­É<yIc_pqƒd09 u>ßÉuaË?¬÷a<`B+X‚¬40’8FÖ$ÏçZ£«”QE‹‹Ä Þñ?G¸ì,ù¢t,{Pü?)qÀŠ~ËJϧebÜÂ!Bl´²? <ô×ÑUçЊ®}Iˆ†,•»‰ÇDê:bKõmÁw¦ºC]/#£€£ KßõëÃŽxÝ¿{ð^m<è„!ì#,å59צ_%vW¿A¼Éó¾§ÔI5`U4˜.È\c#0HB ùáb#/û}†óË™À} š¾(Å,‰ 5-úK¾gŒÌ_¿Â—‹Þ­èIÉïÿ`Àº5€ÉÀ}7s¼¼¼Ú²*6”²›õ;¨µÄí²8†˜Tì R¦›úþ¸ð‹ŒxÙ†"(Õ#0`¸Ò³tc#/©n0–€ ˜RÔÜ°˜I icÓQ‚z´öÜ73¡êê]s#/#/-–Ûƒg0†#,ÝræÙ-"ˆcMF#†4@Šxf£ûÊ#,›¦XC<¨Øˆ°au#,#[â#, çYŠ(£²}ÝGoË>?Õü6ºsÒ^´–%Êg#W Û:àuÞo\™P†FäjQ…Í ^êB „éi(SÙ»SˆF%D‰Až¥@¸Ó}×6ÕD ©ùˆ¼æ§nôh‚D‚˜@`!b…Œ!E¤¡àS@ѲY˜#0 ¤âÐYÀ¡©ÄP2‰èòð¥åpMŠŽC°ïñ2P1׫šž‡wg‰ºª…‹"Í Q‰#/#0¦KgcÃ-צ“ÖŸ•2í¢zÙB#/›v¸%¡&aYçƒmÂr¤W´#,ÅÉŽ»tÍå‘¡#,ë¹!äö$Í°Â,„EbZˆÈ1”ÔPa®Í¿£3¿ÂKï¶ß}Z(+ñ7J¥y»[b5¿KÊó>7Í ¹ã¤Þ\É) Ww“ªR"0qUK@„raÔÔ³•Ek\Hˆaî*…"­7k *#/¦â7¥ÚUŒß2Í0dÍÖÕµ­¤âqrcš¦\RA„%>â=‘SGÓ2Fا7fÓ½®Àe{•`3ëò;Úgk-·¼þß«ŒGa[»•X#/"£í¤_vUcŽ††Ú 14®\¨#‘ l6õ¥„æo!©€Ÿ$ݘ÷BþÙå¸â·HF˜…Îä§á¿Øç&±9/¾½øáuÌEP+S—"BLD­4KÎJÑ”€Ô9Y]ÝoDÆÁÕ&1- ¤æŸg§ClI8Õ##/»pm¦æ#,œ„9dfÒ““ªb®,+V8Ó!´(ÕçeäÎj@uåÅÉÑò³í$½%,J~ÌéÙF0Ûêð­N³–S©Ä¾ÞÛ‘>"ž5_¾­5w"cÙÍÓL|3’÷àÅ”žÙ>±ÕNÕ7/¿üÿäeryøúï"fœ³S°×b#0ÈĪ§‘‚Á%$L HªÝƒE!Kh6-€N bÍ¡æe[áÚPxéL‚žÒ+b*h‚ë¿Ó:þ´EyÍ»¨|;¿›CpV ^½6äð:´lt°PÄßÕé.`*HÈB'†%tîT!œ¯]€Ü²Ž‡+-ÒI Ä#/Tƒ¢ým#.Ž^:!–ݧVyPzÐû±‰°¾‡Ùæxì™bždOGw*±¼‚K1)¢ôä È–Òó·5ÊóΛ¯<¢Ý㶻CÎÖºÖMµRZÒËžJ#H%HTŠ2¨”c(A#0†~¯Ùì=†ÒŠ `b?á@íÝ[ø."#/6¤Ý%Ôh¢ª˜üeÝ×îð˜ Q ˆÅhŸ2V´ÒXÕbÖ¡CèÉžÂ×íë#0mÝí½u•ŒwEÂÓ.5[jÓëí\¤¯2ó7Š2m¯.w$F¢‘pÂaË#Z]³Z4À¤Æ˜["ÐÁ4!JRPP‰l7¢mpZi‰£–cÒ„Ö©‹³P#XCd W6¸Æ4òï)‡GDÞAFm‘µ¨#0)‘³'Ä/ñv­46i |=âÁŠ¨…€Äº"¯’hbÙlÀ0!”sÚ¾ˆûÎvÇÄÍ‘!…RH nA1ÔìÞyTñòû+,õ#/ÿÒ#/Cˆ—Wžº3ªaæó,ŽîX—ICLT€_Âe8bPQJe1+›´“©U==BP‡LàZ5_™¶®Í¦“e¤­™hÙ-&­Rš¯Ê¯Ömú­i2ºí¶wså·+•Íahˆ}]iØPoü{†?&ácÆå¢æîmª`xýËrˆ(°ò쵚²lÐ"œ:Ž<7f^Ìqà#0äSáôÏyGSGÀD¾—#õHC¡Í¯Jpƒ{¨òÎ!W–Ñm½Uàd’bŸ#/#/ýÉd[Aeæ÷O…†¤ðZßS5¸Úa$Ù6VˆIÊ^Åì‘'o׳.‚c†þó{¾äúÁ…_(›#,æ ´¥ê¢fHÜÄéÁ8aA—IJg£3ÒdˆÂEF1œ5×=9…eˆ˜’Ùå‚C*b’˜£FÀÓ"…ÃL;™*ÃA#, •·4ß¹ç@1mËDŒÎ(ð/lƒŽÇ*˜Î¯Q¸QÁ É”p® wXj‡j8õðKUØdéo#m „Ã#·Í㉠ÿGœ$Êe©{|ªUË1Ïg3·ÃÖwˆiç|Ç‚¨>Ì#03…Ç]§§·ë¸zâñ‰IØdÇâƒMÚls¦˜FœÅy‰MÅΟƛ¸lxÄbpÏ’6‰Ë,Íðئ£rêèáR³‘4tC“áB³š»sô˜ÂwÕD“Ú’g'áØã~s³Õ³“Ü„åÀ‡.Ô¿P”:ûÍCÂ!š¶#«)æÁ… €¢’Oրї£–ÿwlû·ÌÃs´Èg«|\eD±ËÇž×';~W{vßrÚÅöwF¼“ ’Ù…ñ2Y3U UT•"Äb#,Ñó™Á`-q÷ê|q–#,\`¸#ðìû õ`´—T"Åx-ª,,&†W–R´U`H0n¶Ñ;ŽŽL€i‚0A@‡Ô2à ˆ$±ÁÍ)PÃi¡ô^¹;ŽžžGN>¡93‹¦e•`v~Ÿï`cø7I«Kgd• x ûÀKóx#óð€„¶U†¼Xd›Ü\1–ý@P:J‚¦K4 瀉µD1Îë÷0¬Ó™£Êy:&xèZDzš<‡ApÜ\!%ªÖ¤·ˆo€sÑñ#,!¦<±D:‘¿µ¿DeŽßOÃjH*Ëùì44v #3^øŸsónBD†’EÑÚ£GÍ¢¯WUCní«®[—bÝ]e¬ÒÚi[E©IK3VV™µWU±£\¦^.I¥»jßdB$„`ÅŠ¨È¨²(';MO!ßÇäûZká(ŠcØ™m™8-€‘Ëõ¢l›cÕ|2ÉÃ=R‹5ix|.æsEÑX|l% ¯'!ÚA!}M¹Òa7JYU%D]3ÿ<Diôˆƒ#Hã"GQ›I†0å„hÕ‡åÈvêµgC)d,¥1§ÈkpŸÏ¹ô ÛG#/^m¼›v”bmÌLoØ‚êYÙÁî7)‹+OX6i^¦oöWY·`÷¢ PSöW˜ÀÔC/J1n‘âtzu[y¶Jíì`ááR D½¸é4Ôµ-Ì[¤è­[ß *Ä`†*W)[íP¤ü¹×I5É€\oVºˆf?jk›#ØßÖ_6#/ˆ$,Íûcû?Ùÿ?õÿ¯û›ö¬ ú”)Zr*5®m9¯e#±`‘‚’í‚|;i-R&ÑñÒ®­ÜÊJBˆÀµ¶&Œ‚¤?I¬½ -£«&ÐÔ»©ªe!^HÛz#/€5  Î1ص#,âS5Ä_,<NðÚÞèêcñ^&-‰ƒÞô6\ñB)$‡é|“Ž#’:oM[·"ªì¢ñù§üÂÏ'îÙI"äso¹âý·¬ïeRI%äÍ ¶eƒ¶$4Éöí×W³ƒù_¢ÈÖñp™³ ÍÕ˜wÉ„B÷Lo«#,{Ъö@æU‚<ž6i´ؽ¶#/Çîb0ˆÃ¼;àÎäºt…#0;tšB&à(y—IˆõÀ²RaF|‚fgRHŒç[å”°cÓ’â_Ìcs¿8¼hbi³àj¡òÈÿ­0¡”Ìòž‡OÐÔï #/C¦Ó=‹#/?£R3MSd·†ŸÊ¶+Gžõ+¡c(ãö¾ìi#/‰are†æ;v'\$™™Ôú¶uhøh-‡(¯JÀ»{hÛó±J&ëO³Œ!èàpbl+{­º5”‡By©žlù $|Ï 3r//–íóÕý„™I÷ ˆÈO¨‰™ÔzO^ñ1#,ùtì¸à'ªI-@…A(œfÇ‹Õ­¢¨Ûm%Qµ2Ú“kZ¼sj£mrÐ;ã ±ØheÝó®]+•F’äBZ’Å@(##0üØÊ«Ùƨ±ÆH1<Þ|OfŽ²ÓíL´1Ȥ†£ØŸ´’˜ö{zÌ ¨Ø¬óè-Ãê8á´ÁC/®ºÖA¢Ÿ…×qƒ8—6Ð#,ºhûF@H#0²£ÄJRA†¦ —žðÝÝi:9†<áÜPŸ˜|9©Ûÿ›ô°Ÿ¢ USF§_>ýÎ#,t…Bt(9˜ï#0Jðó©ãåšxiáÏè±i0ëöˆHÇô8ØD ÜÀæûÜæ'²ăöœ3Ž]ûi¾„b1¨zh{+qQ “Š„ƒs2ëG/Li1f­ìÁ´›*ìMSmÚQoöï{m¦ BoI8öCÞt>iO¨å•É³>ø#,F˜LžmÉ ð cß${ûªõàÎ7r/H™L ”iMAÙ†RZÈäKIn°@àò¤#/0/¤#²äAA‚èäHÙ‹"#0ùAZ#05+S`Pd$ncñØáέ³0.’ ;n#9†ðÄŒCdT`Œ,»”\n)õzüïK=Myz8ºéÏ3 CXšJ$J²DÖ m°bÄd&©¡=Ié¯^q‚ùbî8«e¹þ¾Ê;*d–Gu‡jùš˜ØZ¥„r‡—K%m,N³÷6#/äO3È¡ÝUÇ_—ÇÁ>^1çf%‹@ …ó?eÕqÀ±aMU<ˆ˜‹c¢µÓ?ѧ¡uPoòô„^Àêk#,L8¿]؇ óŽ W=AȃQ%ÍtÇƹoW¯>;å¼Zæ×5Ýq†#,ѨŽ@Œá”Ÿ¶´ø4=¨Þä:쮤SÆð«x»y7^[®¶uºfÚJÒ™›FŠ‘`,(ª #/Ê¿é‡#,ia1f4Á€Â€ª 1’XZ18a¡ÁQkPt Ž#/#ÁêÚ'ª(Ä8:5£*`ªj$H%UJˆÌ°*Ð>VPÈ ù*…ñ΢O4ÏÝ-‰DÑx#0E Æ1¤6,šéW7Û+¢Í“V’®tƒ,‰Å"L416„íhÊ2Ì›39APŒX8•#0*ÙE&h…×—ÇǺñ¶e0›œ­ež¤¯w•u¦Ê™Lh£R¶R±,3LZfÙ$™´Ø¥³2L5²–—½Û˪äÓ»œÛ’¢›®®UÊvë¾.¼kÍçxMŠ•C"å¾Ï{â|3a“LÔ<½/8ùíó¼øÛÌ”CU«à#/†«7L“­<Ù¬¹†J”0Û)ÄræëŠÚQ·I¤L5¨¥‰±Ü¨Š´p˜h5ó¨¯H²4®ØÐ÷´IòãÁ‰†{h·“`Ø@ÜSr0Ó\3 PÉ´7Ó¦SÖ9Ê~¼½·•ÕclÚ½µÒ,fßNéY©-}Ò|W“×9³2Éj±m¼âé%4#‘rÈZ#0Dbµ› ‘(Ák†Œ†@JJlÂV‘ˆR+†š…U´Ã‘‰ cxÛŒ ic$I˜Ùi-+Bˆ‚°Vês[`Æ6$ÝLhc3µ1†o×—8áaÆð1#/[¬¬ÂÛ2qŒÚ¡»±×#Ya ‰¥Z &LW=y¶²C$_[#Ó>ëL“aB4SÑÄ£Ö‚&ìÐôÏ.rá¿Ü‡š¸M…:USÑÄåmLÒ‰aãå1‹< VÅÙpªVÌš‰³-h¸i-<LEà X´Í··ª Ä¨¼1#0°0 ùdHøšç£A~óuÍ$ú˜i‡ t3õ_#,Ç¡ü=NZÈ=Œl€f˜Ð¶ôæ$™Lg5R¤…¿Á4+¸&[aXÞDFF(ÎX²C‚hÇŒò.(²dm†cfÙ%Š¥¦›ÖA5½oPš{ywí»BaÓZU‹f•1U—¬rØ©Bø-Mè1œmávßW¿¦³†=™h¡…#/Ы(~ËÒ­ÂR.°Ó:-1Q'*Vác(3*­Ûß›› Ûñ —(Š#,c J""1!¥ª‚4†Àƒ@šTŠƒ†#, 1(62ƒE!D°6HB*–(»’`—!$¸”_÷0"fþ=Gœ:£ëMM þ¼=_N“;µ%—UúÔˆ¿ßn¸—võ#T9†>²ÒÂõy‚¶©…ÆÅ\ï|Ó&M{kc¸{+åtª„Ùý)J‚0‚$žï›J‘Rš-!v&¤OíéååX®z懆´\¶Ã×mç³wS%,íîé;3…gž¨õgÑu—ZÍLlÌêÔRʳ\Y’œ<KÙåКVO>ºês_Èû«7u¹xr¢2ónN£fó¢ºç3naC-‡8ƒFbh¹,½cMK¡‹2Ž9Ø-¤ËÒèøk‘¶]W6ågC‡Áù§ÃvÜÎýýæ‡ ßž¢è ÑFœvb #ËÊ´ ÓF$·ì(¾‡Ùf'ÄÚØäõ”yÓÜý:³³¯DÜo¹¯..Ýš„’×5m¾0ó‚ž„Í7eES-#,¶Ùj¤3«žQÀ®ÌªýöRÛBp(­™ÎV¯o†´ˆ¸±³˜ï¢Ú¾ºpÊÐM/<p;²5S)ñPô‡œ"'Ÿê˜yÙqóÄ|ài¼XŠÆØJ¥·×_ÄÚVω•°´·³d¦2(&‘#ð¥UTƒFĹx7ËQâ”rI¤m!¡ùæñæ׎–7Š®Í[…¶å¶J®ñѱV5y×.šº¶å]Û™ ¹xÚŠu4–6‘¢‘•6ç#0ÛhÕ%eSzÛo¼dÚÒnš×mמxÅ¡6•bM 0#GÁ‘Ã"+ŽÌ«Ýb£¯L±‘IžÜ'3Ý„ÖE´’+H€ae¡QaH4ÅV kÏ$Fº—….øÙa˜”_;#0žt—7+M|my3D¶ö‚D-Gߦ5Âm¨ÀŽ$¦ë_#/ª#E­çv¥)ªIþcªŠl¢¶+ ™¥65ÆŠ™¢¢Ú+¦V "™Q²li”“1•MmLÉ`#,D.´D‡‘J@Ü{z}øŠFFIû¿‹ç]"HÍÝ÷µ«^üA¤¶Ñ’0’Ù}çNÛý“ìð¿w™éy-êþ^»—?¿ÍEÓ¸°x&z8m©6Ž1¯^ÓÍA#%k~³SM­%2´_ròSfÅUùf·âV·#/ZñµÒþ+uQLóuѪwvék»IM¶¹m£&±[º»JV´Ö·çÝ_nÍeEª»i·n»D¿í½?20&€|Š4ñ©'(1L˜Èeo2HŽòЃ±vàˆÚI #0 ™L£ƒjRÜuóàÑ)#0#/¥wáNbØH C"áÕ×O&‹ÆCbÚ8zÖÛµ=ãåƼóP<×(ÑL,‹ö>TVh1.Âê´a2"Àùf›’@úÎñ$#/ÜÕGïg#/k4‚èYŒÆcsñ²’AŒc+¹’i¨LcI0v™mM7Gša™&pæM¾ŽÓ)Ä¥µÞ0nÒÂ<ý›Î=~™ÈÆ6ºs+G@Èh‡C&;dé¶*ßÑ=˜ãTî°…#/l‡2}åt—#/˜U{FKB˜ù`¨¬dŠa>G;ಒsàÄòð€]œæj‹MÇbižl¬mƒÈ"Vz˜uÂjãL»½žçƒ¢ÁWøÉÛB±X|‰Ñ€°è:õÇnåd>—aºò¸Y6©²q²Ùùñ´ü|hUcÑ‚ rb1ñìšL약R¢‡ª2w¾d>‘v¨3 …8À~„#0­‹jM·×Kîj×JÒWöþÒ.7¥G‚Ø™AÄ%å‚Á °  „°¢ù{o9¯;]~ñà²ïè¢ã>-H€  _›ûÊCà΃õ&o0À¨B„"$ƒ#0)˜T‰"©*Ñ-ÄTñ(Á¨4(4c5¸ih%µh(hûžÌ1ˆ~0Ä0T"Ü«ŠÙ¨,d˜ÅÕ^,ÛÕî*¼î×`r•l…AQI˜YeS'kŒ%XÀ¹$c!d‰8 ÊƘÁhã1ºÆÙ”@F€Ó0m2ѶjÔ¥ˆÒÈXÀ¨&nŒà©!„À¢À`DÂ…",P¨Pµ±`4¼sˆl,.#/©±ó¥¤µ]¹ Ì@/\‡¨>û×Ò )#/1ˆ‚#0"0õÊy?_)P¼æð…í*K?A÷ažäEµNµ´ù‡k[×síI¥ÙªÜq±ÂùÇ †´É“*ØPçgðéŽrÀx=~ê¼Ö«kðʵ±[Wâ¼Ì!è@\U0‘B“­ïˤ÷9üãVHý”T1?óüÁã#/q¼8ŸT2…Ï›+¾3t~_ç#,‚Œâr™êÀÓj*‘*˜z*¬!»}o¹øËãN`Çá#J&}¿–ËWYÏ›‡ìPQÌÒø|S†œšîݹþV]ÂÙrñ™$b¢¶u¨:/p;b”47}Tên°ècëLô‡k‚€ohD%Xi¦Æ{Î)½°3Ÿˆzè-í{ª뚌?«wda!ˆkTTµ4–ÇœP[TR”<ŠV&ÄIV†ìªR[.-‹Þ¨—ƒ÷UÒ9å³”ìðëöu–‘û#»‰ãòšmzŸuM“pùC›Ý‘¶Íêј|H©Óœg *zTõ#,¢þ§ô0ú¬#,1íç¯oÒš.îuÍUËZîÖ[©nÕÍ(bý±µÑP»{Ô”!QjõÁ2{“ùŸ#,ClnT„Z *Jd„`2˜ D$ö›#,ð?&©ê"¤/~s»^.ï;¬Œ¹U”uÝâòœ·2óq´ÙdJI¨M)ª<j»Sl•Š)60xÝ#/6æÔgé¼Þ]¸ë®ÙŽt.Wwn‘\¼[Ç’¨å4·“ÉnÜ»šÌ±¡ÛͶò­m¢²Ucm)ciå¹K3IRVy×t—Nmݙҹ"•;®Ç*ë.u›FŽj¢×j[sÑî EÜØE.ª¾´õ@.˜& {6úwƒè²Àö»I³•Åå@äÞ§wrˆ#!ß@@„íb%€ãöøˆ‰Å:qQñýs”ö‚§`áz#0A¸ß‚Á^9>˜õøBzŠô_S_m¯„γáê!ç˸R}´ P~Ou·'åì9›N£Ø…‘Gt ©"1Œ’H$‘0i¦†r*MÄ€¹¤‚²È¨Z‚ïû‹–?¦#0Œ` œéöñ°(˜@KÙ5cUhRÐËF¾b!‰Ðz£}…!è#,HŽu#0@„¹Ú§]wu,¬XÄÝÖ»Û´FcmU64Á#/ÚWrÄq"‡¨¥@̆1 ÑZ•È„„$O{¦L’h6K嵫íëuËW϶ÛÚãʾ3ÕJ#d"Ò#/¡xªØ„€©E#JE›"Ø #0(i„H£#,ÃC¦{7ï\ÄÈ(O‚¢Vuæ…Oðâzƒò±HaJ¤b°J0=Çñ ÐõV¹gHhôEW„Ad"‘#,ƒ(1"#,$ $wxŽÝ~&7:ˆ)³1b°€AH0HÂ1DT$Š?1âÀ7¡°›ïß½õüê*ME¦XJµüâÚ–!a#,`& yÀ€õi³Âê=•Æl‹ªˆ‰)Ê#,ì¹B™«ýƒð;ƒÒ5$“hhm=>ÞwØŽ¯I¸éôw{¶îü÷Dúî$bˆlGü£ÌÏÅÿé#,¹/Úƒ,5ÁOåNA¬ Ð¥H «™!wD¿£kjråØ„B,”;;Žt½ñ{†1ô¸~Ñ°¿‡yåâšNÃÌÒTž8.Ãü#/4ø!k6$cC#,jH–:É-CJ‰Ç„"@ä@ØR&7Øä"åÆ `Að6Óal…0(F0#hM£{€¨ìZ„%D!#, „Xó›e46<ÇBP «ÚÅâÕÍŠ¹R[—Åš­x!$´IòulUW{²óŒc[É­™>#0Ò{^,wgeUƒpþ`¶´ $a˜ÃI!´¶Òàê<.ð#/Y$5¯Ú*2É“9¡Y ©Û¦`…©íã5VòUvÚ6¨«bµ[Û)TT nòb™ÄÍ#/qL±ä#,l)7nÜš… hã÷ØGòŽûµ#,¤óÍSƱè†÷è~EãxQ¤²D*¨ ¯Æ ¡XL(8*ÔHCZ‰ ¥¬gáåN¬7ýÐÉöÄâHÆ@Ô Èz =[ ÑPYT+#,‚©#/¨UÏÝè#0~("g‘•õE™+¯m;ek–«Ì·šÜFªB+NÆ$b‹HLЙԿ9넆ðÉ=iÒÝSzÊŸØHBÚý©øýí´s,¶´0Ú ´¬Òkù) ××*ùµµÄ_Wœ¿ZyÝz׺¥Ý+S\DVA¶Û’Y1ÁBHÓUѬx2<jf7d„T®þ¼#úz†Ù¥¡EX3“¹ÙÿUdàÙÀsP«/*­¶Õ{´ÅyÜè^‡…šg‚=Aš>"û'å±¼6™¡ä#žA?ƒ1>¨êÄ<e@#/³óUqÔèÞ#0&áËdD#,¤‚ˆOU#/0XúåHŠî«Q@éT{®f³‘s:loá#,p·Ú¡ÀvñMonøÃÑCÂwCî,UÒ#0™FŸ·­~ãZÉ÷·—äm&Hhü#,TÐ-ü¿Ñ!;íôÎe½E}Þº½eFŒ©_‰u%ƒhžî÷8~9,ÂUp^¼`ÀÏr(„£"o>lgsI9vIqÒè¾céƆm ðWùT/'a­½÷BKâê`¸@Ãu8ÄÆ^‘–MìŽò:ïÞU*¡É¤èîÍ‹Šá1sàÂb¬wE3‹®XˆÖ™Rú™u(2‚¸éR «†AdÃ6ŒH–Ÿ#0/¹;]ÆýÍŠ%ù$ü?9/Òv{ÈMI™˜©„=©#,ϯ^¡#0š¢àÜïdf#08®xTÏ\s,®hG)jpN[˜Z*ŒI&‡áÙª©ÍŽÙ%슇y§!#ø>ÊÓ[ÿ¢b˜û„YGjئt-ª9Á\Émgó2ߟ´´7£L€ÁA€ŒM+¾»ß:¼g.9׫—/”Ðv8€ ²LGtÄÉ‚æq¦!…“Lf&Š©­6w$îàÕÈhl0¹}ï]Ößߌi‡A‰6kQl €”õqïŽQÖ¦—¿ù^tžpÁo4—ÌìWÁî'to°s³Z Yù²väZ½»ñh%É iŸwMã.²º<ðme›¬2¿‡ÛÿUãÑ0™‚Ù6·\“!Ä‘Öä7}éf£uÑ¡˜„þѤA°^~Õ®· Ô=<·ŸóYK¼ezòv‹GMc’pC\"‘„hx•)ù›"ÀÞ˜a?šÆá2ðæyÐíòž²SdžF§¾Ég·Tã÷õÏçz¼ÒK¾“Ðõ¤ v9%PwO_—-µ§ô—;‘’p‚æD>ü´.Ççì#âh(kéÎ’‚¤Û)IŠøo<MÀß©&ÔDèÙþwÛa2ˆñÇÌ„öžtúH€ZŠ¨”˜A´ÄAÍvœÊ:wuñc€:ž9Úwô›—m­×êoÙœºSV+3T#î`#\{gRB’Iªýÿ°Ô#/Ÿ¨b#KöÞ1ëZ¸Qky6Kl—5s±iz¤‘œ[3WoF©Kö±úç¸}·4ݘQikZWȬ»õñëé­¼3¬¶',+ÿ/[ÔÖ%NEúrßÂ6+-c©šc50ZÙ±N›Ô‡líÖ8Ñ‘‘#,â¶â%>6öca96Ø5#0ƶ’"HL0!#0@Xy(ÁÓC2aE?=~¹a¼²>nå¦zæ«% åñ&饻¡ô¾ ð¥@Ó¹±£ÛÊ3Z.ZŒ!Ø„å~{w‰ÁU ™8¹ë‚EàœÁ‹)—~ÆØ5ß#/1P´ÔòX¨&õ½´ôæHI$š=»¼x_D‡nOì"öã~‘RŠMÎ#/(Bd Ý @ï˜LFñ§ßÕëV_*;@' çöC#‚’É"±ëS±!ïG /í‡Àù…`Ýí #0„‚$",‰…Ý¥ê຀ûìn‘ꯤûëÌy“FêÁCoW Ç =£©«´ö§d¸+ñó›˜hà117#, ä{|EÇŠ;¾ÁÓÖ}¦cÝ8v‡jqñp‚ñ¢W—ÎÒJTÇ;5m[|âZí-´Úüi^EŠ rPöÀP|FwÆ‘rÓöW»„+GAÀã —g%=]y¶œ”DÅG3 Ÿ&85ìÅG¤â0‚ÇÄF JPDHu©}ø )"*B0‰Û` B‘ˆØ‡cPηx[c=›œuj:„F7Wû\ô!:RЬLcJB@s…Æ-kçÇ÷ÙîfVXâõÃøÆ‹Ò$¤lèÊÛLíR‰È}»uð E(ª@s‘ #,XAâøÓyí¥Àžn˜½Ã—ê7l;]P®’81žÆßã}ôÐk3>çVgS9ÑÝvôcP¾î)¦‡þ£ò3!NÄ#f^i¬–tZK…õ⇠}K­o™‘+Tê."—mßô¿†ÞЖŸî÷^ýç(V¸³ºþ¾xÁë›Ó±öݺ\çF­¨ªÊÉÀ¸P¯œRtÐ:ÐbÄ„C¡h“Q¤D·[–î¹V&ƒ“$!ô[ý_=µµ#/,ÑîÌ0,Èü1OtOéËÆþÏ</EQU O²QfÅS‹”’9‘/·.±)ŽäöoÖ;¦õŒªãML:p@/SÔúáéDÅÂkÎ}]–IƒB?d3_ÃÞ¼#0Hœú•I¿ÎŽ’1=ØSÊ#/Š0ëDí=x/lBs­•'l@ÙÓÙRŒ?fµëÅÍØ}Y"‘DDkˆ‘ï>­ñoÞïa[F8î®V»ŸÉ»U%Æ*Ùl¦–Í›&Ö g^íl†ç•hÉ0܇Bä±4ôíõýÇ ÝPˆ(>”ƒ*”ŠAd#/Š¿{øßn÷ØýïÍyæB›&dfÉS¦Š KD¢ZJ4“J1”Ù6&…!&Í“e HÆH'Íwìžj€Ÿ¹Åè‘óôEõ&ÛÉ;·^gݤ“fݳ;ãl4&K2É„m1¨ŒácX¹¼sÊ#0€F˜-°ïRàDÜŽcÖ¬Ž¯ xƒ¦m#0P!¤”*­kê²ÐBͲå!<«×YJ²«E9ª`Ä»bÈy²sTñ(BŒ»%ÌàL #/[må›™ép¿<6Úé°¬0»v]ìÞz”:20;÷C±j:ÙÇÇÚ̘–S[B#Ixî<WÈý:´¼Ê‹'Ö81ƒ/Ã÷†èf¸Ë¼5H6 àÖÔxÕm &kükß}jsµ8|ÉPjM7Í Â~ôDH-e°U1’•ºÆÆsnur·¶—“ao ¨­]e™Û¤#5Â2`‰È€ã”#01»²’´ ¦’PpÙX1¬’¢R¶Ì² ¶Œd#/q/[À$ð-+tJ!vŒD*!ò•6’á«ûf4„ÛzÔm¼ëµ¦UʸréF' £IQÃ_^hɵݙQÄO&ä…d¢@¶MP#0‘ÐØÄ™Ve®ìY˜Ùcyj~#/DÇ£F rV±• ^Ïi®«€Òg|‘»‰±q4ÖTñ• QÈÌœƒš<êKøµ‹#/êP+½M¥aV#Š) ›Ã¢œSCH°y¢¥ Ñ®#/•ͶÒÁ6‘\#,ÄH³#³5A”¢ê–"Š‹uÊ’‘¤Á¸EX/FÞI £§žÕlFajÁ¦Œl„G¸­˨§4U!†Ón9Q dj&Ô4âiãBÍj¶65‰¸IjÁA´=#/É„#*‰•Ê«N–;‘;1F9X#,#0m„+‹ë#0…\ª'bB²Z1Ž¶†ë)VèÌ%(ÃRèÜQ(1Q°bT‡K.z:7#ÍÂã€Þ=ìs.îö‰««¬ÍbÈ´î4è6ða4îñÖ´³Z3Ý<cfó<Ñ^m8xæân$;r”¬jŽ2´±°°¬’VA–¬j5ÕÁ¦f0Åd‚íÇà×u j0}=¶³Dº ‚ÁµÃ8a™ïXV2!êôCMSLŽ¼V)8ÖÔZž7iŽ@Ùºc;ëV`7p¤8Û˜–0O#04áLƒÅâ£#¸TqV4 ê23ùN XÕ¤ãT`hdCJ‹t.h„E³(5¡²UL)‡Hš:¸C1‰ªJ‚k?¼ OE“] 51hMâ0v«@‹²…Š0:Hk%iEFW® #/,i#/†"g7$†#0aL0"WYzíãk²§vÝ.›\O•äªïá0tÄÝß#Ã2*ùH åáˆlzé寥´¬[ÌÕÖVòë]4°µê©NØ ºCëhÖh\Ž‡6±dZ*mÉ8~H&Ý¥åΞà›4!¡òTk‹#/\nÁnFXÜÝ'Cd÷'œl#/^B¢WÌô0ÚÎPA’ï‹èëÜK#0Ðæ"%r³óäÌ5)t›F¢*¤'¢Q#/£Ó\‡¿Ô“]šäÖ1@‹uË4¨Ž%EÛàÕƒp0<]5¸Œ6ª$ˆ#,h‘±4ΦÑ%¤lPD—$Ò#0AP%‰*("ŒŠ“#,Ò6&(4en ÆÚ#/‚ š>ÔÑ"+øo8tA•R‰iCsZ½ï{ÍtÕjïzºýþÍÐ{xŽí;’Ü·;ë¾Êþ¤ ÛQm±BÔÙ´Í–Dվ˕ª•Ø‚€û;A²Ÿ¨q^‚ÜLž—uÌõæh¨ÕLUBƒvà‘,uà#0ê}É–7½è¨ƒí”ÃJ'•lúdªÈ¸'ß·ßÔ•T›FÚšf³muš TTÜBåög{'²Hžº”•#,äwW´M Í×O¦Î00Ò,PL …Sëw¤)hÛMöÚHDvˆ75¥Rƒ¥ÆÞ.ë·“ùM¼÷¸ÙÎØŠ66Ý]]*Ûã]t¡ÝÑå5©~Ýx+ц%#0¹˜6#Š.ÄXÕ4é‘BTDûä,u8PƒC-"¯cG.çE8 ÚS®axòà}kan…†ÀzLÛPcìï%p—ç¶Íu>WÓ;$"ΊLß•ÌØŠH‘fŒà*U‡Æ:A»„öÖKܧnx_8Äp ïmº¹'&ÀêP2ùÿItfH“›ðˉIÀÂ\µÊ"e :`]ÅÌ’²ö€ñ4L#G/ÅÓµÈì‹ÛkY<nT’ô\¢öoE²ÓlßMyÎU>[:Àþ#à—@ËgWäGæuaÚˆ7Ñú}Ï¡ØÅWÌä“»¼]qo{œ'ôbÉûÉ|õï¶ýZ&„ŽôЮ;ù„a#/&xQà`¡Ä’$„®ïŠt‰‹–o´é½¦ÐïÅì½CŒ¢q‡XÛQNÁX,å:M,TÙÚPõEB¢‰¹ƒÎ·àpüuçgq@êðû‹ êËU8ê_Œ€“°-‘Ó™Þk¡6j€L¨°LU _¸Úý¦ÛÚùÞ/ÃÅÜJüÝÛ\ýLª£A÷¿Ì~íÞñ­Åê²DI†s‹6´Ò8zŒ1ˆWYUh\¹¾çL7é‘؉®3¦“oÌë,#,ä!lšŠ·üÑÀÓ+áMÈA˜ÒPcr%-'«”jõÍ·‹]5¢ÞµnÄ×'UxÚ5Ë\¬hñ¹µ×mb%Y#/cQ­äÛƯɯm;¾[‘£Y6Þ«…Ñ^/ZwoUs[¦Þ=U° Ð嵃DÖ+å=Áu7”Þ‡OPyŒP½‘£P²’!Î#0 Ñ;Õ¯HúŸãÀq"ÚHÁßÙ·uW¿ ð4 àò‹Ð)ì‚RÁËîD?i @=Óùé%‚"; H2#,UwO·òwŸo5Ékvik¸ÛuÝÂìÉ(ª¡;{‹…wÐiuH$`Þƒ`14P Š¤ˆ²"¢B(´­­t›¥kø_m·¦½ÞÔ¶#0LA»kÇ‘PwDˆ#0ô@ƒBEPwÇâ¨pÐE6M ÿ×#,O8køD<ÝTvé(ÂÕ#0ÀÀ"^)ˆ'žŠX˜ÞT©å{ZPuðé‚J^( ¦b÷bÌ~ã¹#,Nï#T¬´0C èį¢¤NŠƒÓ°2böÄÂCâ P ûF©™²I(aŒjY#-M%#0Òch±e))&d´PÍ«Fµ¬m¶5jm6̪j52ÈY"D"‚I~áǬ»ª€`¶³ VÈ‚191‚•È‘>¼)pn{å{å×..W:uzéïk²ä•:ÃLb(U¤@Ž™1ҮěCeldEˆ’ d• (UF LÒa¥®Ûš•’­ÚU¦ªæ­T’«Zê04_PDj "@'Ëóåk ¦´¥¤ƒZbªMIÛˆ±„F1@ÉV$(•‰Œ*ÑYhuÂâ¡i¶¡Ѥ‡8ü´Ÿ6óVtû¼%þ_F}5¥Ï-R¨¥œ@õ#,„„R‡€gy³ìÌÎ0ŽÜv@ñ8C¼0¦N»>#/–>Øé---,·´bR[â3b¦EyóçË­{¥¹RöTÖ­ÁÉ‹?O¿z’Ý$B¶C¨A¢‡#0—ábŒ ¥V(VpµD±êN Ö‡hëîÙ!DóIGãÏ.ºk«Ë.Ê›3(_¸»ÛUÍFµŠÈ""@ ©Ib!Õâà–9©«&ïàŸÔ¬%Ž#=&(ÕðtÅcJPt9ö/ãŒ>×C$ÚT›´¾“}‘pkqŽ6ô‰?+–…ã\¿Mªgùºo _Kœ‡LTæ'ð$ØMÕLƒ/_<k:F“%#ú•ø¢×50tÆoNé¼*~/jKtý·ƒ²z®¤Ž.8ésÇ•ºÖ]¸rSÃÃôv˜'¼aq<Ù9Ü¥<ã~Ër/¢Žø%aäü\µÂ)NJ4/Ï‘üÓtç$snÍ(ÓJlç|]?«^PìPË8i­cw%òxì;ZkÛíŠòÏ=ÐìÍ?5·fyFi="•#(ï½7ëFf£ŸN{Õ°mŠn=ñÖ¬ÁŠ©féKG»ó¶ü0NJtwÕí¬zæUjW0‡É]x%]pÃ?‰…†~/ZYLÄ’8œn]´‰ï§¶@ßm¹‡¸Þo«ž'vÍ#dN8¢!ÈfB'‰ò{i®–Ø{ÕÝAÁxNjg¶ïrÖ¸n„$Ç:“pTý2üP@ƒœm÷w =P>Ï>~ù”šiê=uv‚G³Ã[ëéºFÉÊêRP¥ŠO5¨©PÞ@]‘AÙKî¥7ÅH‘k»ÄzÍŸIbW(âš­É]Ï'mOßbt±G°¸˜P-V¥©ë…ŸñõØZkpí’^½ºL¬ì©^_°ŒÆâ^Ü—Ȧ¦ ¯d…p±p//Š?{•–ê}œ‡ »è4ûí~ÿÐ6U;:´8…Ìu!ñ=T·!ÕY g ˜x[M¶ËÙb«7 ž\è&ÕøP)ŒÃ±m}£‚¥ysð®¼Ï9Æî’ižÉw¿:`p†¸¨Þ¹’˜èIÖk8`Ö@íCƒphe0Ôl»oÎëò泯•FŠ†|?1 ¦Ö×LÎ „tLÌFÛqVûµáêi[¦6=]Ý:†ŒMªJ;'G£tÔ♳ËÂI$Ýp™æ·*«6ó BÄøÞÔXt±ÛFúe‰q<G&ÔÌS¦2ݶÜޘѳŽ%åfÙbªlChâ8Én>Q³[0HVœqÌDmŽÞ8² Æw+VÔúÊμS/‡Nmciã‡1Îßæõ­Ðûb_zë¹Ú̼’O/šãŠ$ÛO#0JíŠÄH‡K¯‡aÅ!Ë&m­'8’öݪðíä+4ñ{éTó^Â¥WœÏxÑ¥š=Æ8ö­Ëv6]¸–Æü‘~¹òÎm{¼¨®mÍ­Ì_OWÇ›ä9ßï—²²¥H{2a'jKðóžßtÞßKåå4?V÷’yÞ/µ%`Ün‘å‹/*6ãŠÏw“§xÞºkn»ž™gÛ3Zxô‚%ÝûQæô‰FÑQmÚ•mœòœX\iK­‹c©Û jùÆÈwäéSÝT;íþcu(òAèš ¾´ßJü^R·òàêê­ì} æëÒcâý;qíöO@íêÒŠ!DH‹ØÏÇ”qÙcÝtàwž!ã;¦ó¾›®L<÷Ëxk‰–Bgky ¶QKÒs—:”2c~…7 ²›¹³‘»<äÕM Ž(yCCzÓà3]·L„#/o2ÉJy¬oÐÆ#,ôs0¤Ó>6¹>/žÔüañÔÔ#0#,m¢‚ì…BøˆK¼0°Ü»@)-]8—„0‰¸Í3ÕØ™À!ú"Pwü×r.:y`ăx^’‹,I“ZcM‹°ËBz×Q G4¡M ÛFyu &v:šöàs(°.N ac†ÑNŒTÎ9$aJ7õúö˜\¶ nØØC' 8á¹:>“öáëÄ-–4"P½0æ6-rìÜSp¶Ã]ØeцÀË-—‚ìIÒ‹»l!†#00q”:‡íT)¼€¥ê §!#,¢\,"B" $G–¦øhŽFMºxW^Þ¼qË;%·3™BԳá #0Î(çÀÃ-ÒIÏ!®£A26`ƒa™Å„#,¹ã,e¥÷mɬb+"guC+¶‰®&OláEuU¯š2 ²(¡PÆȺËÓììØöÒ¼þù9vl:8Cû´Ú}Òpó-ËTãc­ÓBs$b ƒÜŠŸ‰ ¨—k÷Ä! v×[68Ì©ßQ©t¸}Ðíâ}f£OÕ¶êý•ç@­mÌ¢Gh´®ª&PâÜ©oi0%ßnúš0âÙt[Ox> º7VµÛ¦6)ˆ0Ûƒ{özl(#,Ä?CožN‹kJôNS3‘ö9WÏ=$%CÖÌC[ G¸qÝm³ï]#,ïÇ÷–‹„ds-ÅMVòªw®í’¢Þ0’‡×C‚Z–3oQŠ$ŒÕ©`ãˉK`VZŠè§‡À¡™ç~%‰@½wÙiVkQ-29¾ysæâtÍÅh«†R±Ÿ«–”êâ ˜ÉNtÑÑì›íÓb»6ã–Üyß[á1†+ú-ûâPÝ›$Ë®9˜Kª3lÚY5‚ì[Ô6¾%í^ÛéÔp_îÁ½ãÞ?.y_fêY€òÝûÛžRû®×Þ5s iÖ¸¯)t·Œ©j&cÇxÌ™;%·’Þª|Ü17—”ŽûxÁ{ið#Ü'Šfüo˜ÌÝKŽ6ÁdxAô•µj…q“ÄÊŒ4{¾+ÕÁË,JX!zšZ±-4#0n7ÓIè.®q¬a­H¤óD³íÇLºW2‹$¤¼F—px3„੩Ɉ ÄšÁPæG™Œ†‹Ö]ÑçFO~æO79?QÛ2[ø²:¢zö€¶ä¸Âa#07Öœm—≰6'g=JQÕYqp‚Pg>FC"…*ÒWƒÈð(šZOÅUî]j§'<°ä‡¢î§`öëº=¥#0PcžAä1íí[*‘ªº…CC^#0<‹iõq3¤:dYí¥ZMƒsÊWpÔJ(†Y¸ ÐˆX ŽÐ*AN¤µ‘ÚqXŠôè¼1;éÜv Âh2A¨`¾ä]0ĹŽï9î#0†4奴¾¼lŽ–v¨aQ¨#0xxŸÀ§OP!»Y˜|¬Š\Sض‘<,G@Ao««Q׃5¾ÿ¬+#/ó‘r}4Ó1.ªxƒD@¢ÞlY«}=Å”>gÏh 8Œ#/ž°õ¯alHd{RÑ'W|ÊÀÇ…¶úDÛÕ‡¹ÈâN]~'ƶ¸¢‘U"GÎå±gv#Wìæý’½«X­Þ/jÍç$Fzô/AÈæiRÅQ…&F*ÃÁ¥|kÅ×&¹8u]Û;­Þõy½ñ¶õUűn\—u¹µ%å0³D,Þ*… ”E_T–Û­âÔ[sj¶¹¨P¨¦ù¡Œ,ª i !€¨˜(* Šˆ‚¨£!R€ƒÚzûkâqÛ¡_M…(‘Àu<™ÙØÕ®µ{¤èF,g9¨¢‰æ4¡†[%²S%’Új«_®ÖŠ¶ŠÚ®bI6Æ£B‰ê×ö9·m+ \«x>èf¾—XñËe£ ¥¿ÉÞ*Æ1ƒF(©QL¡ÎùáF–†àA¦í¥ \1Ò?ë~™ƒf–Éà£i”¾iUÉ7b2õšq† V`™ã•Õ¶ R–sã˜òh%Š „… Ñ]R¦EF#‡Ë=HïXjµvDF'`žT‡”¦-&n¦ôRW„Q¤Úm*Ô‘K rªÆÄÆ1¢AJÞb†Zjdh˵…KwL¥ÒHU’ôúË`~+Forrn¨)dÀL$Ô&iÁ‰e—vãÕ•ºÃQ7#,£Œe{èái£¯íe›ÜŒÔÑFš!!¨«XôÖµ¬2#/ÉŒÇqäp¹ròq‹ŒÕ²·Ã+4IQ#0mئ•P¸s@ªÓiåÊ:ˆ&ÚƒVpDVÃ$Îe¯Êî«FúÌAhsS–Q—„l†-A’´Ÿ]><®ÔÝ.›¶NrdF=Ù¦f‚0ÓŒº€,€4C¤#L¤Q!͆–U7«™_ Ò²d¥k .õ±ã#Ž.¥p`Î4Ý€ÞéÇŸh­sb‘'`›’Â7N^mâìÍ ,F¡ö¦ÊDä0x/V†5ßPW…ÖrÕº¡¢ig'D2V0mBFᔪ#0”*à ÖâSC¤Á´Á±‰¦Z6õ¸¶òd²ÊíëÎÅ›[ ¹tøÐA˜Á•£cP¨­tx›I«ƒ«„5±£Tj+ÍŽé» ¦2X6R˜`oeÛûÎF‹Ã‚V²÷Ø;Ú#0#/Dtaý»ˆÓ’?/EFúJ÷ÙÒ&¶µ¶ÆÎӬǰ¤‰±·¼°j°dA¥i«ÊД]Ñe…±BÛ@â2ëF3BÒÑàSÁ„í¬­ƒš”Õ*¥Ggm¾%‹Œg ™R‡…eU[HAÌ Eý‡#0<AZ…LÌ}@eb,hQêE®ú-xݔТïÞš q†àÊtÇ2œAø9­ºÐøkoK fR˜dÔ€Æc8ðM´h¸³#0ÕÍÃ, ‘¦x;„ŒçºWk!®§¨eÃ&ˆ#BÌðM•ƒ™•UÓ3‚-:Ö„D`º#0†#q©½(iZ¤q`8 qC¡ÜKz™dÛCìÁ– íÁ‡Èù…!W›Ì˜ŸEßä %î^sO§âàûZ©L"#0D`¥K.è60bmljŒù‡n¿ïˆW¶¥#,Æ"#"2‰ä/¥`B Pâ”4•¤$‚y×?‰Á( '“ëi?1„‡t#0UNê±Ç¼dQ¦«´þ'b16ÛO hçïKæ˜~¥çe~õ:I3ÌëLo-D’{N°¯h§LúT4úûCt´k-Â#/Èééìðç™;ÀÓÃ}`x“ww_~úgf*1+Úrqfâo.1Áù¼¿R=/ï±´”)v©\\¦gÃŒÞçžÜ o Úc“¹0ˆÁ.;·fÍÝϽ_²0’Ç\èÕáÀyšžp8Lhlˆë“EVomÕÁ(œüÙ„?)ßÓÒšœ8<¼:¾ºŠ–Å tò#,ÃR½˜­»6ÒInªe5e$hCbAªÒI¡"èfÿ×1OdGókÙ=½µcdá÷@dÚ¥EM&¶° ²Š&J‚¦’©•³LšÆ²j+V4”m‰6“c6TXÓMšiP²M$¢”¢TE SPj%"XÓE3FÉ°Ò©¡ik 6aV*"1`B(°’ÎcÄùŠ÷9$!ËÖq:¨b/®%¸Ð{z'c9n>é¹]Öæb{w#,¿âèlüFÆ€‡#,šo§ÑÛmy½6)'Â][A<Ã0 ²¡d‘ ª'¸‚[*¢_A ‘ª ²šôíH—u#/aŒÉH)#/“_G×錦?Š}O!&LÀÁ!„Ù·püLC¸®gNÞ´0Í.EDÄ– ùø†ÛÉ$…Ô:#/6^þG¿ÑæÌ}û:áÏÐÄ1UC)C•´vDûóvôžÍÍôM4(ÕÒúY#,˜6ü#,ò9ÅRFŠÈ”F©6ÑGè-ZæÒ-†š­ˆÏºí\b¬#,…ŠJ¤}Id, ËŸ@`'£9¨Rˆ‰(I#0>ß^apk*ÊÓû)pÇ¿‘pl¡2"?|(µ#/3†çæË£UŒ!òqtxÔ:TQÁ–ŠjB9IˆŠo$ cÓÔå +#/4­ 6<ËRXÒ+H4{˜F›¦øy4vtùóµæQŒ¥:T4¸ŒŠb2Ù)¿mɌУtff0‰XâÒ¡cc#0:@¡…B›P•äèuQ®8âôžŠ ˆT~w ‹’‡$ñP6L1RHtÅ®½—¿gâºóÞ6½mv3£ÖDŽ#/\þÝ9VÙû&Äs¯¼ÜŒR©dXzÈh[ЀòhG£Ò¢L“¸ @ç1$PP†æj©g©–A¶(*¶ˆ! `3!ì6%¡~ÀˆÁUY!º!#,>~Ü× ggšb¥*…$òXH*×£±°žwîÂÒd;¬߶Ù8îî03ŒýiQ<ò†Ìñ½a¿ß…ýo׉1t…Nþ»dÜòòV áÜ=¡ñÌ$GPÁH¹åƒA<dˆÔ”BÍĉ†´kQ3óGœ=ê¿cÓ§µ92Dæö¢Ðñørðþ'„ªô\¦%]»MõöùúM$„…XŒ;¸é#8V Ìô‡DüWÞQwáLB¦¥„tyÆÕ¶ŽÁlÆXÀ™¯âdyû¨âQùÕI!ò*RœZ#K3T&¦1¶™2¡± ±I¢#,Ô¨bîˆãÉvW‚¼­9:Z}^“×`Ò'OˆpóÅÕ#/k朓•®J4óêFEªèÀÆm{I " ‹ÞsØXvc»;­Ë²LÏGÆM˨ꉜ>“-¾J\Â;Ò‚„ª9“^ ›ó0l8Ü`ð,ôMBÝåö8¡¤ÍžŽj'icµbkvPjCQ¡kôüóÝØ:U&£“3åËï„À|¶¾Zü†ý¿Ú¿„ÖV[d’/?(ô/mRŽâb_WéÖî0cEQÂÊîCLϵ}Døf?ă@Í“òWJþ.èœhε,…WMÆ,ÇÙS;?%äyCt¬=0x™1ŠÃOië#/éÛUÒÝ€xw¶#/éO¿ñµü±Ühl2nã#/oMõ⯤Áë›_wC##‡%íùMN8Ó¯2FrÂ#/š“Öb“.‘+Ìèå*F‘£7_›¾qxáR#/xyp°Ï‹8)O ÔU[ÛÆ3YÏ]/}²‚`llÃDAÁ¼HÒU£!#/€&vÄfDÁÄ@A"j!º«ä)_å¥-ƒ+YÞ–Ï(fÓXkZdÓC|²Ì— Ë*aÉ9ŠË'8d±†í1]¥kÎå°˜V&„#,ÆÀ°ô†Î &©³¿”\ú±5,lÁAqªPÁ—KP7îÞh`ÎÌ ÒÎÌp,Ë L6M륕ÉäÀÙÁ5ˆÓ„2¢˜D#/#0r;»#,¸F@0)‘‰š¡ˆm±J@ÐÍL¼¤{ņð4fÒJ‚[Èm£®5êâÕQ¤b%â‘ ©´"؆ùÑÐ\Öô²"DR$[#0á·õÉÇc[ Ï bdHE+²#0o¤Æ3šP´Y&÷f€ ^ Á”j£ÏËüÇ­öég&ü2*Ú 8#/1ê ÄÆ™¥åÇZ³Y #;°tÎ*WšÎÙ¿×ÿ,œ­¸€Ç«¯¦&Ò.À&XpIyënÊLq‡É^›÷9)‚X{OPà»N’Š#/Ù­ŠRèzLË3óË* ,s‘š`ažtÄ¢P…(Åx¥ëIÐdék¬X$€ŒbkJ8A³lÔL+¢ª „„yEÉ4ðï*|¦«‹ ñrMMÅñŒä¹—#0„"&Ëi6ÞöuÍiMíÇT›otÃMˆ\ͱ ùE8þÊU(,¼s§ÃH²±¾U× Ýæ4«#0Öì:1i€$3œŒáÕ„Œ½Fa|­pMk¹uœDu¼¤S­9;.½:b#/"™H‡'‡I³wŒ1¥—ÄlÉ“ètHë6³cZYå’í¢ÝŠá1 ë‡ ´«§$l‚ueI¶ÜŠUf&o±‡f\7M2ÃX=uÑu¾!BÖ¦ yk.¥5)X‚.]7õêH¶BE÷wʼnžŽ3ŽÛ‘#0º¼ ufý¢µo¶&˜ZÑx)FÔÔ|FãÃuná…&LŽÞ¤¶f,%×"kΠ«8c÷½æºuÑjhˆCľÌ,ÛSuSà8W‹™Ù4Žé·r°ý£PX²#0ÐÕ§„%=-Úq;I4²!Äs×HC2T¨âÈJkåÍÝ°ºžÕaÉ–g‡©sv¦&ì¥Çœõë#W'ºM&MÅb)”j7f$m“6CŠvÜÇ<š–ùtÀÒ¥ÅÌëg€qÅŒ«¦n—Y“ò#a‡^ƒ)ÔÃ]­0R/Âo ·Ù—1p-#·#0v( zMT‚L±0K”S#/Κm$Žvš¤ˆ°¡#0ŒIðX§#/N~ض4·8 ÈWz5ˆª#/ÒÒEÑ]$rç|uk*Édñ ñt%Í!r·(çŒCkˆƒ]j$$˜<ŽýÿRßl±§4ûŒlÔ>ý.ŠMBì'˜ªª¸Õ¾;Ç“f€—O·  Vûq†!DºZ:ÙÁntQÓ6‘Â$üïøyJ!í²æ\6E.@ZòXšh??®N:VÞvßT€¤„@ 4SW¼ïωXèÚ°ÍÂ3¿u®qÍ5ídeæJ¸½_B2ßM¢0¥N#/ ÂÒ X{<ÛFó~BÀ™aud¤Ö †l”Åšw–“ã¦âBZß‚3®R¡Z6)¡øf_—;£AËQ1 €Û0UØC˜¡*tžOÏii}ƒ%cP’"6ètà.{Äâ“3™%ÞåÛ³·UjÐ^0JNüÊv#W·hÀ!glÖªÉì(#&B†£ B˜Z'"¬®¢hÊs;@s° vh¦›¦‚Ë.ôŠ¶¾½f­å2CÉœo"/9%‘â”ë“à-MJjsÎG Å{˨˜#/ï¹@áƒ:¿'(ÆÐ4¦øÌa3"ádzJcMös qí”3C°ZèM -<"=1vy®*߀FÉ;ѾΩ††Í‹ŠS“qßbf$’ d$lâ\+H“p3Ç©7Õ$m13”p†Üƒ¤H÷0Æ*šxÐQ†’®*”6MdÔî“Û¥²Ä8:òMhœ¿’ÈÕ€æÌ`Ž›³0ìnð–bEÌÆìZ aH¬NQ™q0º\˜X°#/¹ŒšÉ¤ài)ŒM†TÀÉ(³e’™ÄÜ4Q³3,TT –]Ï7Òu*ö´Ï„mÔZòÚëŠ!‹ŽÖ•\u:]¨Ê4,¨6¨§d15°l&d1™¥!™©@—c& ¤0è”;x¨ÈÉÓ$"àèšØŽ©A —.;#,³a€íhªQ#/ŽDÔ30X op,Ž…%1#/ˆH;”Cq Aƒ9ǺuŒ™IG!›¡,J(sˆHT]S%BÂâI„KHXcX" 'Èž^}'ÀÇ­¸9EA¥¢¢"€ÇѣÜ$Þµ÷x/¤laíä@‡Xhˆ°‰ 6#,ˆTT ‚þÿQo-o`ñ:ŽMao5È:¡~nÚfLCïô™ÞLÏœß i[Êý£¹2aÜÃoPä†ýY{iJ.Z6¯†?j\£¿öJ\ÐMöÙ,0aÃmTø6ÓÁ›BÙßõ¢“õ{ýb]xë1Éî°cùH&èÈå.‰=t"È#À†ú ýUté‰RŠ)Mj‘áxN1׆v1>Yó†8ƒH¸©AÜÔLŽòå8r¹2Å"hñ•¶ìG饾¤5¬¼­ó‘4É/ÆÙQh\Ù€:ibyGò'·ê2caô“œæ”]#,´à V)sKë›–†è+ѸÜíMaö=ju³µÞfàªÕ»º´¢ð:‘GÒ¾šÿL}#0€‘QõÑÞ|Tè8aºDƹ¯¦±ìCÛRÂO²)¶Ên1qè=ù⼂#0á!œ4„ªFNp°Z‘ƒPlr'D’töÒ¡ø›Ì`Tí%‘1,”n2DŠ‡!‘(×’jwÓy»¨•":òÁ1±ØBÉ#0A‘á@BˆD#0‹eY¡##,…Ÿ}#$ÀúÑð[k«o–<wq˾Óc˜Me™1L˜"2m °#,ëŠBbŽ“ÍÔuö>¾ß#,žÉyݦÍÈÒ%ª œC@)³êiŒýJ㈲p4þ©Ñ*$JR1RÓ¹™™!˜"¢´Dƒ@å0²çXL.b²)“_&Ešéâ%ò„óó ¨. ‚›Øƒ‡¯;ÞØî9Ój©<.së#/(ìT`éOe²«ÌÌ…ýž5üå„ü<Ç¡œ?P@z/†I¸hƒ?sxèÑ9g\‘ßÝrõ‚üú5®L…Æ&\³ŒË}©©÷2¦dÛ”dÝË(`"p„è¥=±èTKôl¹à¼RBU­Â¸GSÊ­'¯ÉÔ~ŒSFU¶"ÅÆP—ë†fEb©¬DôÐR?y¶¾C^”`amí0>2˜@“ש"'¶Ðê)(Õ–Õ¥ª‰/Ñ•’Ú©5Fµ¾&¶MZ÷ÛvAäKJŠB{aeZ‰ ¥’ Ý"81‰b15{"´'«˜ÑT †3üð#,Ò &f½¹VßFWè5÷}^ß}­bÒÍ&ŒcXÐB"i{÷÷w‡CçS5Vº™Ã €?B0b"‚) lú0Û·Ú_u6#0Ï>A°\Ú0ŒßôõóI{oéé©ï$±z/|0À(FŠ2BW`@2`ÉPÒ!A¨àL–$’ä ØT„QŠŽÔH™q6' À"­"46”2Z„¡‚FDBÌB „³B¨È 9 ÔoÒèãŒÀ$#/#0ŽÍ1C!Wp…¥#/nbY€ägl¯_b‚v˜ö<ÏGšX`BL+îÀø~£è¡Öi5˜†äͪAªÕ½rp­œÃ¼œüƒËÂXy“p8‰$0#b’$`#,Ü׳–ÄKÓRaÄ£ìjYó*,ßC8¨º!"€£­B™%Fi‰fûEZZ Û'—ÐwAu­uÎík BÀ(x ÅÈLHÊòþÏÝ]}œîL`§·€~V¨b°Hr”JÞ0ªÓ!FlÔ°(ΓJªPÄXÆ»ˆ¨ÓÑj H ’4–ÛR÷Y"p?\™Êᶈk¬§c¾:eOQóõƒì ª› "{ ‚A¦š µ*ƒ ·Â6÷ô¹%÷¶>9u'Ôy«îÜ‘‹d-jÖùü¾Þ†•›Z4ÊË5$©¦¢c†PVÖÌ6¡lÖ”U©-ý*ˆKB…H B‰Ît:0äû%8ú2bº+¹7Ýo|ÍÐO;¢Ü|#,·O1EÃÉ`õ;µãƒXl‚H²íͯ‰§£%;²è½Šnê´Nõ=¶ê ¡ä à΄’??~þ0k:[énìƒÑ…³ü'0ôDѪ‚#0âo}7Ö=u¬¢õ'^„Ö´0㈡4Êv&vái&›ºNî(2@Ž2Â[¼–6PjâÊk­@æ“dÂI¦´[·ÂF#/ÂL²Ùn±…5°|ŽÜq³!´˜Ø2ãBÒgR؃`˜±ÚóOFW%$™C¬éjQ V²ûT#/¹"’#, ²ÒÄ—P.@æ†fe–ͱÛå7À¾r@ˆÇ«º½8›"ˆáYƨèÙ‹ŠšÄ@9{7µºÔ™07ê!ÃäÿŤNîÛ!ÔoÒ$‘ ®¿;Ì %„^â»Öwt¡JU®ßŠ‚\Aô, „'N]ÆþÂ>\ý#/ØÀi/JÕÕž‚b˜ÀzA˜ `D`C® #, Tw=qêå‚p÷ž6á«ÞQ#,+˼ëð< alTðÈÕC‘ãQœVh¦Q+âuóîïÁ"Aº(]¦Xk¾çÏmþ~–+vQ—f3î§Lé#0/ÜÖQHV„Ö½îi÷”j»Ýñíõp¬Â„w"Fî>¡Š/hÙÈ*Ú*ÍÛo÷‘jzIQš~Õöàû÷ƒ'ÎN CÞȃm ×áQ‹ ½Ç÷PÑÒîcšùûŠ¨øq¡O¢&Ò ŽC²(£½_ïˆôØ.Øbuñ؈3^_zŠ0„ÉŒÉç]EEd—vº)"ÃL¯¿ÞdE~ôç¾^y{ÜVpÕ(¦ÈZ1F%y7Y¼“¨¬!ŒF库jfÚÝ„_Ì¥##0`!úR)¨EPÚÇ-Û~Ÿqáã( £×¹O ñ0>g¨< +M ¦#ç6|¯¤#0z•:zï¬$Y‘£(ÂAÄ í:Ì1H(È #b¦V‹%–d¶lÚ¿'çŠú¯ãï¼ih†6Tm%FÒ”ÒÚ¿«ü]ÞhL¾kØŸ2*…DLþÈJZÛì™t!u<ÄFÉi ΄IØ?;“Q0(¤eQNȦÆ*&Ö[E¢ŠŒÛ3Í{çž[RjÒÓhNº-»Ä(?š…úCgý/G§¸ž÷Ïôÿ ƒ³I⯈A6l#/Ãè<¾zÔ×êÈ©¥ÈYìÖá 1>A‹{by"À‡`i€p@Êr2Êaˆ’éÅ>—5EWªÿn1ab)$Iͤú#0¡õQàgHb&7 (ÛhÁpýP7'-=6#0ŠîˆZ³#/0ˆÀüÙ(ú¬‰ˆ¡Í=€TE_b P#0Ÿ¾É Þmh(‘ !¥FˆA#,Š‘¥&©m:œêúnÞSÛ^ßkm»‰€#,‡ôIv(Vè68+MGØ‹#,øÔ*OWO?AËƨg–5CH~íµï°áý¦Ã+ÓšXm¦= άâZR6Ÿš„á#”~¤<ÚCi´Pçk+\[iJ8çÌP?vtÆïs¿}ë6Øeú0ÝEŒLûÌÂécÜ>ñô~àä*×Wëm}z~^7dѪY¦&ÉF£5(¤ ¨¶2j¡M£[ñ­x‰P£j(ÚJeaRÛñþCj T U#0#ï!‚ŒB}Ó&ì$b!T²€„a¤J=ÔT¡Û¯™ÂÄ°¥Û°ÎøOª;×n²Ðw]ÌR#,¿Ú"«WšÑmF+AjPª4&ÔÙb*Å­M-e_±×éÛ㟧¡¿ÎÎíí¥VY) iÏ×Ò©ô¡L ¦¿¦q¦¾Œf µc@#J̤Íø““$ßæ Ã'À}<†ì-¤¤»¢ÎB-­YÚ#J’F‡F†3´ŒgöàÀl(¢I¼´²P(e´O’nÓ`Úh¥M{›#/r–Û]Ý•ã^o+]Sh²kz«’¢Þnë3vfUuÍÛ[PÍdYjòîÆh›]Ý]Ý­&Ê’¦D¦Æ·Õ¼Ó^uuy×vÊœ« R¢#/F*6Û(Á#/2,E´¦–É©2ʹZêÞ]]yçko‹e™V[)kÉ·Wvë,f¬ºk¤ÜÔêɶ Žáª-Ré H…„Ù‚ýQÏl“PæI8hr&Õ‰A?Ã4¤&#0_:3Ú T¶®ýO›Í}4g3 tR¡ùRa1T,½ø˜1©›Àá€!w¼*Ò£úµ601yçd¹ö¢ëþ£Ž_·#,× .ÂIÐG·ÃbzpOK7¢°ŠBºov1&òy›i8°-¶p §fe°æôêI¶áSŽ[¾'ĵ‹Ô#0¦$l²Æ¦÷Ò·N4À¦¦ssÆú¼ŒÌ=rêÏ«o8‚EW(¤g"ð¸7×°˜Þ9¢¿Uì!$Ôè"F\ef^ͱ|3¡w~E‡»)%þ":3 Mäÿ¸ZÖ:>É+HéPyoÇXO©#/!‰åÄT,¶µFPÓ#? Ö±ÛU›Ôç¾Iêd!il…3$Æeðª|g¨Ü5J>ë7í:•#/ñÜpÅ`×L‹áòÒ´ód›¢>Ìëw¤O¬àÁÛ }й(R5œ,#)7˜ÊbÒ¥ ¦Âàs Ym¦fÇÖBl#, ÝE³F]#,¡ îL³>nþÇÈœJº›%ÀE‘/HàC²Æó‹e‰Dh¥§º³J»ê7Ó€îŒaú Xbå 8=‰pâµÑà/ðùI(¿DS‰Bí¹¡ñàËdAÛÝIeÊ<‰£t ý´­I,G#/š½¾FY)a1æ+Òt`fA°)¥KâÅm¹^„æuWÊ­mæÚƒp8QC'øhY‹jr•Ò‚kă#t[Z…];S‡G~ºÜØáÂ\ a9Q¾§S>Ò¨roaÊ!"0ŽCTö /m 6‹' — Hs<Ÿ’¸™œðÍC2Ôå¥Ç5À8eøryŒ0îI¶Á!#ÚqoÈS‰ÒodCŠà{!g#°ñöfb@ÞäMO\5Ê¿g’ÿÑzúw7CðÄ4#,  Z ý9{ñ[Šp+ÊŠCp'V,œþ’ÈD?À¥#° <'’=D|.~ ~~í¶É㆚^ù—rb¡1ýUm#,ŸôôÿñüõÇÿýOöäþŸòÿ‡ÿOýßïüÿíÿßþþoúrÇòþ'ÿ-ÿòÿû·á_üÿþwôüÿŸNŸÿßñÿÃÿßÜ?á÷ýøåÿ~¿ûÿåÿËþ÷Óû¿Õýßòûµÿo¿ýßôÿŸ_Î6ËþŸW×÷ý±ûT틇7Œs#,‚#,¼!œÈ«‡÷ÿÈHˆm%M¤ÁêúØHUÙ2ùz¨¨C™IWÿHþèþ¿¹ÝØ’@“3?·×l¶¿E²%Ë=™ yµÚ]šm×'$,ÆšÈÂlp?¦ÎÿöÞÊa,€cþ{÷íLsÚP\ÒÞ%¹¼Cu¸€ux˜½ÑåZ£#ÜÎ<Šð’ H!Яنcž1cÔ½€V#,5#0ŸêP¦wk|Ï?ËìdÏ3?L¿—jâu+Sb9u(ðÔ˜Ìi¬d›ÅAb¡½ì{ LBH|uû0?àÍUÝÊrI@í{öõ?ùµÙòÎ$|e­1Ìv±•‘ÊÙ™þ‡``Ú•5$Cc#^Í­1±­êh$qƆËH‡® a†Ïù0hÔ!2"ªÔ«ëM"eà;áBdìîµ»“ŒhiyË#0e,¶K Ô=Í<ÐÓt¨£Ì᜺øq›ÔÛTÐâc2,aÅÙ$B ¶º@“0âñ'ì–l,—rF k†Þ±êûiÍ⑳»“Ή%¥é¨C Ê0·—º!žóN¡ì†ƒ/ž™Œ£þšfÂlüë 6P®*f,‚Z"fVHÄb×/;bòÙ#,c.Їí|mÄÎTcæQZ§ðHuo ‘Øáä36lȵétª§A…Ëå—CÖÿʬW¯ýÔƒTö$\ž<ö|г^mæªb»’U˜'4m6&>4“Ĉ˜y†}˜K¸RNEÁBxR²àþÞo~çÃnc^ TZJ”RèåÓ‰]nÄ«ÔÇŽc9™¨" ªž(‚Ñ<í׉n6, ¬Ù 2CpÜ©#/\”3‚”±QS¸YbyÒ4_¥„ « ƒ * §‘Öt(wä¸Ë(D2»Ø@õú/k¥ï|“wù`qÒhfê°sÅÞcÑgy´F#0èÙJS³S~#0ÞO"2˜"‚#0ŒjÅĨ”5QjÊS+lZ’Ô¦’E† „‹R”)ArÊNq#,Š'vgyô‰#/˜Ú0?ØÉ1áTÝo0PE-yàPKµ)+CÙÏÞ#,Ÿ@pÍaþ¢P‰ ÞÛ•Y#,ïIÈ;‰€€ÿkD9Ϙ­?¶Y2IÔCÄý::9ÿ®o&Ø-1ÄÄ%G…ªlÑ•t›?ûoòØh¯¬Lx$Ù-áŽE¾°èèN¶¦ÍÞõ%¾DµÖ#,pŽ`kS•šleÌ®û¡íú]ýd`ÝÒ“lUÍ  i¥SG†fùz1$Ø÷„ùQaNÃ,2U1lííÅÁIè¼e¯¦›×äl¶³eÃ,­øÇW’@Qb“Vƒ*0Y?E¯V+s\ÚM£èÛrÔÒ£ƶõF¯…k{˜ÆÕ&$H*’M##,¦I2ÈY­Øš´½áT,K°â‡'"Œ2HhiúéOî/FîS„d‘#,ÜbèDêv;laðTH"d@l‹ã߸Þt׸|1õ“«jFz)hÜÄO?ý×n¸‹˜œ(4’,€HÍãà)_õžÃéî£Í“ÐŽÔs}\EðâQ‚ú˜pó/Ãbƒÿ® °(mv£¸ìÐwGm\ÙkY*Ó&ºkvÛu–¯¾Ñ6jM{}š­~º(¬Z Eíõ{=Ù?Hþ¾j'báÓåÊwøJ=Þ¹Ž<Œmµ ~.«tKD8èèÅ<¢|%ˆ2ŠO¯ïÅÿ#0Ÿóìûàí&f˜HÁ?øxÉB-à6àyiûÔU'ÿŽ¿ù4j† &0žß÷¶ýBÜ?ËUÜ#/™Å}Á-ZÿïêÿwÞÙ¼‹ñxzþ7_Ÿ~dãW>;ÈqÏNˆñ{Çeý#Çàáíá =‡Œ°·ËçðàxÉá‘ÏÀò¯ýûO§ÈëÿÚcòÿ §ì…ãŒB?ü4»â~Yx”Z?¶,ïgÿšÌµ{åÓ…¥Ô+¨›4ÃÒ#¯á ªßàÌOé Ö•.$ñ‚([ù*Qÿ®‹YÀé]¼N*ˆ¦pAöj¸\eœÁ©‡|Ž!ñõÚtí¢tYXþ^rÄÞ¸bòØKððŠ‰kÊÚs sÇÙÏ,ÀŠÃ$ÉßNñõ]aûQDa›áùþ? ¦Ÿw93êwÕ±íÓãÿ5dxóŒâÅ#/¾“©j¬b/¼<xOí§þÑ#0“Ò=Â"¯“å#,Aÿü]ÉáBBuÅÁ
+#<==
diff --git a/linux/ws_main.py b/linux/ws_main.py
index 79e26915..31d6b979 100755
--- a/linux/ws_main.py
+++ b/linux/ws_main.py
@@ -97,6 +97,7 @@ bp_sim_gtest = SrcGroup(dir='src',
src_list=[
'bp_gtest.cpp',
'gtest/tuple_gen_test.cpp',
+ 'gtest/client_cfg_test.cpp',
'gtest/nat_test.cpp',
'gtest/trex_stateless_gtest.cpp'
])
@@ -118,7 +119,9 @@ main_src = SrcGroup(dir='src',
'time_histogram.cpp',
'utl_json.cpp',
'utl_cpuu.cpp',
+ 'utl_ip.cpp',
'msg_manager.cpp',
+ 'trex_port_attr.cpp',
'publisher/trex_publisher.cpp',
'stateful_rx_core.cpp',
'flow_stat.cpp',
@@ -167,6 +170,8 @@ stateless_src = SrcGroup(dir='src/stateless/',
'cp/trex_dp_port_events.cpp',
'dp/trex_stateless_dp_core.cpp',
'messaging/trex_stateless_messaging.cpp',
+ 'rx/trex_stateless_rx_core.cpp',
+ 'rx/trex_stateless_rx_port_mngr.cpp'
])
# RPC code
rpc_server_src = SrcGroup(dir='src/rpc-server/',
diff --git a/linux_dpdk/b b/linux_dpdk/b
index 4ec2bfba..44d31613 100755
--- a/linux_dpdk/b
+++ b/linux_dpdk/b
@@ -1,7 +1,7 @@
#! /bin/bash
export WAFLOCK=.lock-wafbuild_dpdk
-waf=waf-1.9.3
+waf=waf-1.9.5
p2=${PYTHON:-${PYTHON2:-python2.7}}
p3=${PYTHON3:-python3}
diff --git a/linux_dpdk/waf-1.9.3 b/linux_dpdk/waf-1.9.3
deleted file mode 100755
index 7ad6ae70..00000000
--- a/linux_dpdk/waf-1.9.3
+++ /dev/null
@@ -1,170 +0,0 @@
-#!/usr/bin/env python
-# encoding: ISO8859-1
-# Thomas Nagy, 2005-2016
-#
-"""
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-"""
-
-import os, sys, inspect
-
-VERSION="1.9.3"
-REVISION="826456a3e8d1d27787c240c6ba69d280"
-GIT="b022313c197614245fae732c0dd1d8167b9ee5dd"
-INSTALL=''
-C1='#)'
-C2='#%'
-C3='#$'
-cwd = os.getcwd()
-join = os.path.join
-
-
-WAF='waf'
-def b(x):
- return x
-if sys.hexversion>0x300000f:
- WAF='waf3'
- def b(x):
- return x.encode()
-
-def err(m):
- print(('\033[91mError: %s\033[0m' % m))
- sys.exit(1)
-
-def unpack_wafdir(dir, src):
- f = open(src,'rb')
- c = 'corrupt archive (%d)'
- while 1:
- line = f.readline()
- if not line: err('run waf-light from a folder containing waflib')
- if line == b('#==>\n'):
- txt = f.readline()
- if not txt: err(c % 1)
- if f.readline() != b('#<==\n'): err(c % 2)
- break
- if not txt: err(c % 3)
- txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
-
- import shutil, tarfile
- try: shutil.rmtree(dir)
- except OSError: pass
- try:
- for x in ('Tools', 'extras'):
- os.makedirs(join(dir, 'waflib', x))
- except OSError:
- err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
-
- os.chdir(dir)
- tmp = 't.bz2'
- t = open(tmp,'wb')
- try: t.write(txt)
- finally: t.close()
-
- try:
- t = tarfile.open(tmp)
- except:
- try:
- os.system('bunzip2 t.bz2')
- t = tarfile.open('t')
- tmp = 't'
- except:
- os.chdir(cwd)
- try: shutil.rmtree(dir)
- except OSError: pass
- err("Waf cannot be unpacked, check that bzip2 support is present")
-
- try:
- for x in t: t.extract(x)
- finally:
- t.close()
-
- for x in ('Tools', 'extras'):
- os.chmod(join('waflib',x), 493)
-
- if sys.hexversion<0x300000f:
- sys.path = [join(dir, 'waflib')] + sys.path
- import fixpy2
- fixpy2.fixdir(dir)
-
- os.remove(tmp)
- os.chdir(cwd)
-
- try: dir = unicode(dir, 'mbcs')
- except: pass
- try:
- from ctypes import windll
- windll.kernel32.SetFileAttributesW(dir, 2)
- except:
- pass
-
-def test(dir):
- try:
- os.stat(join(dir, 'waflib'))
- return os.path.abspath(dir)
- except OSError:
- pass
-
-def find_lib():
- src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
- base, name = os.path.split(src)
-
- #devs use $WAFDIR
- w=test(os.environ.get('WAFDIR', ''))
- if w: return w
-
- #waf-light
- if name.endswith('waf-light'):
- w = test(base)
- if w: return w
- err('waf-light requires waflib -> export WAFDIR=/folder')
-
- dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
- for i in (INSTALL,'/usr','/usr/local','/opt'):
- w = test(i + '/lib/' + dirname)
- if w: return w
-
- #waf-local
- dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
- w = test(dir)
- if w: return w
-
- #unpack
- unpack_wafdir(dir, src)
- return dir
-
-wafdir = find_lib()
-sys.path.insert(0, wafdir)
-
-if __name__ == '__main__':
-
- from waflib import Scripting
- Scripting.waf_entry_point(cwd, VERSION, wafdir)
-
-#==>
-#BZh91AY&SY^:Z})2ÿÿ±#$Âÿÿÿÿÿÿÿÿÿÿÿ]á õ#$8E„xaöÜ÷½¾Ï8€#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$¯}W#%÷;©™‘U¶Ò*[·]ö}÷i«n»¶ú8¥9ŠÓ}öwË:,×¾ä懻vÕÜûÞìÊåï«ï½žžöíží‡må… h­»»Í‘&°t ö^î=;>í{í¢_CO}<¸ÛÝÙ›ï>n8>¯[Ÿ]=ãT$ìß{ß}óï†ÎÞ7¾Ñ×ß{íyéÀ÷`ïw7w>»ì#$#$@#$Ÿf€ô£§#$m÷²€uàæÝ·p#$õÞÒͧCÐöòíkf©­Ðjû½×#C{y zØBöîÛ`7³p …D¥Dª=ì{e’B%t(H•Q@÷p:Rß{¥ç¶6±Æœû«=i{…e»QÛÛxÔvÊ¢•¶îu×וèûç½^#$Ùçœë«Íõ»ìhÝâz={0ªM½k˜õÕîî{ëdäåžÝÓ}:vßq÷²ÝÝ^×ßo·×ÞîÏ.‹m}ìa°îz:U­Ølì蹺I»»Ü÷·yêÆ:ëBîÑÝ•†ÚÙ’DE»½ÞÓƒÖ j¨-;`)èy¡{“¥éæ·¶ô[ÝÝtjT}¸;ëÓ统më >€ÑC­m÷Þv+뺷 lÀ »ëm€ß]xö‚•íÊà-÷Ýï{¼¹ÐPûÞö¼çÆîÎY]4Tû½ÞMw7Kì¹Ùèy3mºãÝß]õçVÕˆ©»·vÖ2ÙÖïmímÍíÝwtÜç·{ÑÞóW'‘ÎíéuÜ¡k®w ‹í©>ŸlwgÄ÷m³Õæ#%÷¶¹§kŸyº{Г*ðªÇϱ÷cÞͶï½ÓÝ}îíµçg>òöì”ûÜ÷¶û²œ^öTæÛ–v­Ýi¼µ€Òw¼ïAööÁ÷yä9Àz‚Š®±Ðj©ØSCs;´1‘îw¬î¶÷*Ü4è=Ærw{3Ó]W©º®Qpj{qïe#ºòØ4 ÝîÛt·\ï#$on: #$×½×¼îõµÞ=×î¤ãÓíïfÉ뻎÷Ÿ{Ýh¹Ñ·#%Ö ›Ó]±GÍ„’×ÜRz­Y–ûÝ:}q]ô;Û/-÷Ýsî÷{›O€’ :=æ·weí«“1^Ï/Ro`´s#AŸmÈû›îÝ“f¯;«­¸ÙÔÇÝöï:½–ìÝÛ·É®ÚÉî%ìÇjë&d F¡ZzÞÇ©ëß{ß[Íè5í‡1<|°åïmÞ̓½Ãn×Rؼ#$À»c]8ª£žöô·{CÖ½nèàîÙÓ/'¼â¯}7ÛÝõ½½Î¯¸í×¼ÉuéÖmòì=ïmŸg ®Ÿ@%Û(PíÌÝ«ÜðPã®ðÞ#%6Áçvï\¼÷ƒ¯@]Ÿ@„‹¶Ì†»€#$äqäÕµõ5âÝÀ=±JT(-»¦Âö.@C»=ãw“³ZÇ#)Ï0é7wnvs»9\Ú’H¹vî›d›­ÍïïLÄgS=«).Ó+“Û;Æ=Vû¸|C<ÛíÈk¯£àclÝ::>Œvfê°È–uÒûk;_v÷£4ë˾û©_mƒ»»q¯WƒÞ;ÀѾ5ãœ4Ñh#$€#$&1‚i€™¦™£TýS5Ê#$A¦mCÓj‚S@„ @M ‰&™&4“Ó)©æ¡š“Òz™<¡è&‡¤#$#$#$#$#$#$H‰¢h ¦M?LT÷‘SõO)¼©êyOSGê†hh#$ h#$#$#$$õJ(MM5jmOSÒz!ê@M#$#%#$Ä#$#$#$#$#$#$$ˆÐh#$ õ<4ÄÅ<˜ÐIê=M4Ó@¨Ñ #$#$ 5#$04ÈM©O5)ùLjbŸªfžMQê~¤=‰COP#$#$h#$#$õßæUZüe ý+Q«cN¿…U§e6C>ª­w³Œ†Ø‘X‹Š*$Š#)Ÿ#$ ‚–Š€¾¡0@Š~ƒóÏƵün‹†°Íhüûz³s_ŠO8§§"âf¥T¼'…/1WXÆ#‡ÉüøÌ“´ ZD@ÐPÙJ É70¦E„P†¬92/>Mšcg£#)"îêUŠ‰‰¦²’®fñ‡Å¨ˆš»ÄMÚ—£ˆ©À“#$ÆP¿w/úG:&„#$+iš6™Ý]U¥j±­´–Õ±­±j£jÕíV–Ûk–$!Ë¢À6‰›Â(†0Qlˆ@Ä@H@õÅh€€ª$F@dAPDÈWl2DÆfÆ™šY2!ˆÍ&™¨ˆš‘,dE”£J)¶“aŠ™’h%5MFØÐa¤°S"4M˜±Œ–™Ej5,hLYShÒE&Éh­IK-)M¨€`›- YA0e±hÔ–Á-¥4È&¤d !E4©CJlmVš¶šÚ¤)•C6$ј™!4DV–´f–*JML•-°MšÌš–¦ZK&&IQFÀ†‘²¶B"ÉE2(Ñ"ED†¢”Ö) Ñ°Á ŠCLÆiQjRŒ4DŒÊ0MC"2ICd­$„RÄ#i(d HH†’šVhɉ£aˆ¦RTX ¨aR*d‚,jR˜I‘É M dÄÅIF‘-šRM&$‹DÊjcf@ج–± ,1RHdÄ)±¬šiX`X$˜¨Ø *$ °š ‘(Ñ“JX ‚a(c$¤ YŒ™„Y52¢#P›%D“!5&ÀHD’RV @A‹,Ì6Œi¢`‚e‰")Fl% 3l³YKeŒd,i¦hĦÅ%’6(•!3 Œe, ©f@±%ˆÒ¤–™AF“4ŠŠ¦ˆš¨F ”%‘ˆÙ)2&‚i™Q£0£YH ÆÁ°D2dÂfPR1 š’„Å#$l“*fkLcl¦Ó$!™©ˆR ÖRÓ#)É„’$¤’Å“fÈÚfb&dŒ”²&ͦ#%±")™(lÉ¥2BÆb̦ˆ©D”Y©d¤Ê) #&$È¥dÑdŒSe  E+)$Ä’e5&Æ‘#%#%#@„cBIY¤Ãh4lf2I„²e"2ÄK6JdRˆ„´„Õ6lZ+Bd’’$-E°Z ›FE# la Â2Š’*,¢Q‚´¤ÌQ¬f†„™#)J)”±FDJaŠYcL°"Q”e!¢jÔÛV‰$É¢Œ)‘¢ŠI™­ÉlJY£LRde3K*Y£¦hØ[$¢š"±”Œ#%!M-~Ûk£!²´„„©De±¶6ŠŒTl™2ËÒM,´FÂ6I¨ÚÈŒ-2b6S*HÖDS Í3Y5b$”ÖJBÑ‚”IbTi…ƒIŠZS&LÙ6 U¦6PŠÙR*•JÌ)³c1”µ±cdF¶M2²%Ifmd«-”¥4Ãk"…2#b´fÒÄ–’™M±mƒk cZ‰*#)6Ú%Tm6M´M!T`¨,U‹bÃ6l&Ù0¢*¤)A(¤ECR‰¦ÔÚ#lT‰XŤ,›i$ÛVËm¦CbÂBe2¦¬šfÈÄ4´Š)"ªšÒ¨Å2ÓK3i¬Ò›TÓ‹R¡”I6ÒÍRÈ¢ˆ[BKfL’˦Y5¥©d-f˜ˆ ˆeF’2Å01 H† Ð˜ÀËQjIQ†R¨¬“M&M%€,R)fˆ¢¤–FD1”I#)”l¦hÐH’š#%‹2B"–š’ȶ4Í4"i!€ŠŒ˜ÃL$d´X! !&ÁBlQ¢M“4³µ2³¢“a2¤¢£bÅ•A)‹3I%”(¤1fmÍ£c‹hF‚ɳB’ÑCHTd2#F2š$¢–RЙ6Œ ²‹$–!ѵFšÑš#)†Š–Y6ci3idÚ6Æ#!’@˜È,’XJ’£4Š”Ó £-’ScPm˜lÒm¤¢(ÈiFM¦V³”ÓS4‘ P"DA*RŒ hÛ41F€k4›%1 LD¤¤„¢ÑªQ¨Ôh£lFÆe°Òa¤’… $‹E(‚6(°&ÐVYdÙ) Ø´d,”³If´l$k¨“E –Z‚¢Á’ØØ(ðÆ6ÊPÖ‹A¥)–B£D‘TÈÔDe6ÖELmŒ˜ &ˆ¦e S)ID•,J"Kjƒj#),”Y*5S3E²RV6µ)ŠJ6Sl†²X´4K6d• 4˜¤£$F#)É€Ôd™Ù“)²R›3bb)TU•“Y"3I5£)ªKbÙI4Y QI&V¥”Õ)ª$Ôj¢¬e1i5² Q¬j(±H¥3#)dfDÆ•"(6fÌÖˆ±¶MÑ©*i­¡•b¨Ô…#%h¦k4ÛE£QcV2kRde(²³h•£‹2ÆÔZT°¦ÙFXÔĤƒeFÅ•KclYš“¤ÚÅ&ÙLÍQ£hÃH¨¤¢­¶[m4²&XY„‰41ÒhÑeI6¤ %X­Ø´Í[F5¬ÚMše­ˆ²[SSl…µm¢Q”P26’cÃYS5l²*(ÔI‰$ÊI‘$5“Z,ÍTÇù?Îü—ùÍüç5€ñ÷ëíÿ1+J´4}Ç ·—ýL”“ üpÂ(Ѥ¿–¹¤²ú¯|×óž«×7ó›×«zÄT´ÇþreŠÀ¢hó%qÿ†µÄ#ƒ#) GÈ•, ’H‘0(£#w»KD¸äß‹1ìÒÿÏí­¿ðH’…+r¡+JK ÕÏV^=EŒM‡¹vt3´Õt­˜Hc¶&Û¬-q(ÅË®Ï#)1údop Œj2q!aSM•Ê×DûêäG—dï; _-ñâï‡>µzܳ®¬…ߪêní®MëqóÚæ*ùWËÑ\ÛÑssM¡´$ÐÆÄj×gx(Æ0.¢%ˆ½Û ¾W6Fo[šJ c*è-5­X\r¿õì³üèJH¥¡½ÓÕ[0YB u@©çRÛ*€±}ônð‡T¦\mâب¦¦Ð¤R›ä¬k™"˜Ò»Õ$¶#%Ñ1ˆaöi¦`´5æÎGÝí…h Â#% 2Ãvü ðà¶~†#i†Ë:óí‰/üîÖ0…•¨v.â©ŒøõåáÎt]ÆÍÌ’QlX±Q~9}³E  ʨŠO]Ql÷3ŘVnÒ@)¥Á¡FG8ê‚>ª\‡3k†÷Ö\#)MS.‰mAF#i)&Km±ª4Š4dƒ{œlË ÔÁŸb¬9bÜZ¨¥a7"rpaÆŒ“®éG9¾[Ëοտ¹¼¯*1ií×l}%s¥ßC¯ªo0W좑:5ýN4Ç?ØóÀLð D¯ï¢™³ZbÖ#)€Ž*C#$È JÕ £h§ô`׶~|AÖ 1ýz‡sÒ4¶ÅµCòÉçÚ¯S¿»?Ã˽[iq`m•ù,,¸žWTEÊ5R›n%:¼S“#%†ïãfã¥m¥CXÌu¡ZR#)¯§?ø¼«°³ä›VÜ(äß¾¡[PP±Â0BÂ2'DÓ3n ±j?ºÎ‡§hf$ØM”}h¶åXÿª‹dê˜JJe¡Ýe,#%éÒñôvCÕ¾"[~¼å0’Gá–¡ÆFÐÙŽKñKÑÃË{5Ŷ…ÕEkFÿ­ë£,R,_Ô2Ld—ãë¬Y>/ÓÕäF=Òy¿ c¦Dw¤ßýg÷‘O';Ex”Ö#ub±–õb$ÅqO<Jìiv¦ŠE4Qxæ’Æñ\¹¤YýŸJÀŃàÔF(W±±ŒØ÷~ú™¨(-2„AXW··€ ¤CDI±IóÝs_umtéËrü~u^Kí_sáz±êáƒEËtÛbóÎýôe(ØÖMŸ7rÄO´aFEöT¨²EE|jªJ™­nÅy‹‹&¾Ção¨,–üÎÅÕAhî½F[îÍ ŸÞÔº…$¦˜ú¨[fÇóÔ9ä Â#]+k6CÉ­ê™Uôï×y¹[³ dÚû» åó`ÜiNa+€ÄÐÜdÜEhú±²(–” ´Â˜('m@8ëJn†^®ß%ÊT—Um*íÝV)økÏ!—~ú·w’wvÝÅI<®ÔÙ†¨Ze¤‚0§Xôq«¸y`[&Œ™i.šÂYy¹J©ÛE½”)Ã…Ëb¨‚§>Ú“Í1(Ü ©äû[ò ¡êãˆøFðxŇ¹Ž«)¶åßÌÔ;a¨þóŸFx±¾©áŠ®¤8¿Vq'^ب½­_o—•b©))…#J_šF ýøíLCPqŽœÿªÎ×ßtÑõûhˆhß#)èÃþw²¸3:”… ¤GÅ S“Rߥ}}{­Ýj5(®ˆmìã繊ELbfNw¦Þ|%$¹ÆäFî¨G_½–ÃE‘1½ê RÖ¦·˜ÒÝúndM4ˆópÓò> 0U2#)B­t«$Î$‚TL¢‚E„yêj·¯¹Y€Ù4Ã>ˬ×akÃâ]^üÄÊX綱Œ¿ ˤúò¢VŒóÅÈZ¡=#%˜É0Í^ãÖÈ_•)0ÄCz“ ,>§u@¯×ñ$nÅèÊ£ªL†sœzgnú£êÓ½45ê>—f|=ý6Ó•ºë¢´f©2—$À‡éUܨ¶åTüšL/ÉÉPî¯i&AÕ-)ž5XJN{նМR»• JXŠæîú_üþÜš@C…wÝ3û8Â3ª^ç9õçÉ—"œpã>Ñ[øÉ„=ìë"ÓädMǶf(öȃ^|oH0ïø´Wd1Š’#%¹õöå1¦Å4è=Fj/n{ˆŸ’GÝ—ëµÍ¶%ïÉb½îχ$™Yð«ãÎýé8§~tñÁH"F†'ù«j„ZÖe¼ƒVnœÌBBéùH5$Ï… 4ÑÏrðÏ#)«te¾m ¦ÎUƒ)çu<äíü\Sía®ÓÙïÎ^?«ÒùävBI$Šƒ‰wqÇ] ,P‚S$ÈO~´]¤ä½ê)@Àí1´‚Qƒ}ÚÐQ±uËApX)â.<Ü ^£ƒîñ‰!1ÛàìwÓ±H ¬&€ö7²ÌÔ™ºt­Á"Õš9fŸ'‹nC‰±õ‘8ÐÇŽ-“èü!ø?¯^çxnY̨ö‚Ó»qô“ç.XìãÉ8ÿc¿Õ¿nÿñ¼êuß{ÖŒYTNŒ X§ -L´(¤Rûî!sw½Ôü=u5A'“BÇÛ—æеTµEzUAL¡ ©~|¿Ãyõ{Öö>?·ºó’WÚÔÞ©õ0¤X£eIÝEZ(øPRUê…÷³ÐOÍùók>j×*kV¤F~¶S—ï}ùãôb÷n–Iù"ÛWÙ:n¹ºŽgT¬õ{mr00#%}ß–†{âÝüµWW~¯f#%­fƒY1„ûrÖïPÓ3óÒSþ3°÷iFK*‘A‘èÐŽµ'ÚC#% áÒÑŸ„ÅSSŸ3ƒi4qüÕp3?Œ‘ñleWÞAV;”¿Œ‰æµîÐi ð҇碑B`vë›DQc` Å"ÒhŒê_v¯Ÿ)ù74c皧ٿvp> > *ƒ/u† 1ÆdƒmwŠ1üFvNç®#)oÑvÊcËíšzxõÜtsã±Ä6”$aÁÁif©>‡óD˲Xü]¡2”ô»üž»ïg!ÆJƒu|/¦a”Ë%{q÷™É‘òd¾ú¤x3[…=Û\ÄJÖì‹X>i5;+HÌÄES#)'_ÂP[oZªCÁ6ÈJGïo,r‡ôqÓn!­és9õ»À‘]ˆUTÏË‹ñlÁÍÕ:ãlà ‚W¥ž.!û¯+¶]Ânwhˆóˆ£›#ŸF¨"(˜w³àÂìvôÇNu¦œ¬þíhòì¢#)h]ÚD´ñaK–RŠÄ:3„aaÏ]{|w–Û!\kbb·$„‡ýŠüø¡Ó@]h”!7¤ø'ö9eu1ÉÓ"?žÿéggœá_&Í#)ª(£”´û¤5D<ö,ØI P0Ää‚“¥š:îtTŒ’B-)pàÖ"Ñ­‡cÐOÿQÚôŸÛwsÇ1„bäÎyÖ>5°º?Îáf1ÞY°„ßI[Q[9ˆâ©ˆF òüq÷èrÁBj¯ªîÕõ64âïáÍoUäi b§uú¾}Þnšj¾e­Ï…“?uOÀt ­«ÏÌÀ\Š#=ïzß#)²Áxá‘Té ’BØA‹Çá/.&«s¤fÏÃéÙ1^˜ iwdršòä¬+—onàZ•¼Ê¤㑘)#fõcêÖ¦ àD 2ƒZªHrd™üžrä¥ç]*ªÜuè³´ÃiLŽ7–¼žûdzÂbÑZâÝŽü4úSo"ø!þžÐÆ|¢+‡C÷òâZ« ³?”æ¦;ÌÈ…—{%ø‡'õqWÝÁÃn4A•@¢õÞk4 ¹›9ñ¡E†tͨ·8½ž^®V`{u­ŒU4UnvÔËoÍAŠ"<±ìÁ–~ÿݦØþüQ>Ïë¸,í~Ç Ãù±e‰Êb­â"(3Oéׇ‡N{vçҋó¦»÷dð(¼šÔ¥•TqïÅ“à:€®•\æuð¶ø=|.XC(¸{<m¸!•PÈýT—•óŒàuÆœˆ•XÔ±DÆÔ3Êi±Žëª·Y‘“Ž’‘= §g}Œ|ªª˜ú¡ŸÏTËÑlÕû÷âII•æcðG,Ž‚Û9"}3ð`qcÜQñ“| «LOÒ|Ö=ñ_#†È+2F¹–—ßâ«€Ù7úÊ\]s—Ãì¿-}wå|[ŽÕÕ¸îáÊ?NË®UVzX8Úñtøð³5µœBÙ–z3õ³ÑÎg|O++àî”Ö ¸Úä¶A(ÖI7š5éQ…Íûáµ6ÎÎ5àä‘{zÕ›¼n‰Zïs#)¦µþ:û°yQÚÌ‘Q‹Ÿàé›{ª¢Rêè¬)\]œ¨ød9¹?7MKÁ“M@È•àØœ™û»kfjxëE¿T.õ¿Û{øê[i¦K­`QèAt‹‘ÅîVÚ­B—Ïç‘êXû^~ï~Ï)Û×ÈF\†¼¦JýÑžñÛGYeA,·èšߎÁ-5V%ý5}#C@¿Ru‰p݈—ý) ¸J¿*–ìê#,žPi®ÔŒgë‘þ®#9˜1ï—3)˜õéf#%6•T-_wK EQEž¤ú8 娾ì…yú"½²ßÖÀ¬[ìi7FJ(ÙSôû*LxÑÜìþ|ñKXueØRwg¨vñQfâ.¾j ‚ ãM±sX²O|#)x#$E³[ˆdÇÃySÊïPŸwg^Û²5ÎÉ|Ó¡ÄÚÃœ¿íñÆlm £H˜rBiªêIŒ3Gž^|sÏQï¾G˜m,oD>Z‡“#Y-O˜Ñ?S‹ízï…÷Œè…ŒmUQ9$¿^,d/W¥Ó ÇF¢e¡g½„ nϾNÕÚÜAnèýFÅTý±-ë \íg·.ž!±2Îaú {µ¬ÂØÅ»ƒ—󲼋H÷à£7ê½;ï¨Õ# º»¯…”Ã@Öš»¤YÑ©uÝBQù¬}˜Û׆fTH4˜[òo N?Lµ&° TTM*`½)àÔ‰ÍHŽ#%*uf3‹D—uK­P2‘š’…;­Ñ‰<÷ö¼áWÅñ‚ꔬ©«ž<ÝÛ›Äír)qÕw']a}¤"ªÅ–´Än–ÿGÃÂÏPäËÙ»w2 t•P¥‡gCOaRI;âë"òœöþË•ÑCÒfpþö2l¾§ð?ph#$šsñÚhÓÜ6mË¡r…Ê´uÿ·Ý/ã]îQÁNÅÌ”ú²•ôðáiŸéÖNoxÇë3¯n•x†QSÆPªo½–ë ¢ÞÜÅIV÷TGo(Ê”Æɳ{òk»[aÞwuBC5˜<®±k³}£Nؔƫ7sæ¬ÞêfšJ\ÙBOÔìýz5ÇW#%É#%‘ÝõqK­f:;˜_ =ìû+­ ÊºzÇÛ&ÛºýðÏng³øè䢓J)›Žßù#cý¬¦à}`Ö1|qª›¼2Ý}ŠTÈÝØ#B5ÀP^TÿMNÆõï¡·PÐÆ1ÙT  ZÞ>2ÈÐ þÇÈý›ýùæÿ„0,å ‚Ñ¯OÓ-ùgŒ¨v.>]Ô»Ë+¸Ù©QtB¼Ý¦;+‹žÅyig•;žó~‡ÿ¬Y)¸”gÆwª¸Îÿ\ ÜÎ¥u|ž¾Ì„šv³´‡É÷½¡ªÈ÷}Ó;XìÖîJj9wmìpû~.ڻ㠙¤N“¾wÔ÷x<§;'Î&wN—| Izç#%ùv[ê…E‘[Ç(–JÞÑ#•97µP8dæ•Èj"ºÖÿâ¾™–q¥Ã›~Üb§·$‘ô[@ÀoÏ0Û[hI†Úªø¦å¥ÔŸvãâcE`|ð?¢„œŒž!½¾~&:4†ŠqÚ¾.4L§b½Œ3·º÷ÆlJÁI­ÈN¢ÿãTgßåÍ9i–Ÿ>nI7Šèò›»¼»¢.8“mSZ–ï´Cˆ|ü|~°Ñ‰±È°Õeê¨Ãèh¹Ð4•Ó“ÙØK7(]…nêF!§ßżèÚC!¬¸'|%Ž3Ú6[+ŒaF¦©CÞø ¥Æ\¦ÛO‹\m‰”D¬Ýdg¦š,@™Å^@ÁØ × XkZqÂØÝ79v”¤^õ#$#È@ Qä^ jž½êŠ%©²¥ãm.ºÕž³e¸¾Ýu´Wƒ r¨@Gr´—ŠDŒØin¸_¨f!b®Ü[fÜÙ²RqÊå°\#•‚åa¶¨Ž'ªôßwWÈÏ;ôð®|øt³7åKýÐ'€´^ßÃ×°Þ¶“³¦qÖ–ðJío,×±[”Êí. =µógpaýH­}>!×ó±1ÅG“é-ÿ.üñÌÛU5žM˜Ûïî¾#)|ð$RFÂ/þš:¼²°LsH!üCo:“C™‡D†îOõù7jŸ%Ĺ²L`ÔÄkm(0@§DÑ@c0éo±â‚wÇ¿2òFnž°=\j×ÎØÎÒûqø±œ/]_O¦Û+çì™ÒS˜ÿ€XZíb•åGÞäpA=ºŽ¯¤5¸J8ù•gˆ…áø—o>öë‰W›Ç~{vÆ|Îؘ|}XÞé›9N¨C?W=yjÈËEwˆ-ƒ€±$6¿Ik¯jôn) ?bÇ*dªp;ÇX^îòÉ[ǤÒ'jˆD·x=Ñ­ª*,|iž?L$HyļQXþ/·)t;s¹¨ùc7ãÇ”¬Á¢Jiä8C@ÛNlŠ~G‘\ZnJmG5Vê]ά‹©“#$¥¾fƒN†²½yÔbç 9'Á¯"Q¹Êýa`o¹Rª†â!-LÁÄ|‘T¶ñ´D;ŸDYßëZË;™Á¥¥î·»#)#$–‘·z&iÃíÓë»—f]¤f4 R”þÎwzzÿÆnQ¡³íÿ6íßÆòçB&ôÌ/;•ì?PÈcÌ–¨ÍR‡z¢òÉ Lªö‚7Í»ówü¸íñ#$Mwþ@ÙìK‡ÍHï÷ D @HƒÐKüáOG 6)Æ0]¾Iç¢iJ‹$ˆŠB´ŠÇ]¬0óp4GÀϚšÉ;n-Ê|ù»üÿ-#“š±¡#$?‰Q#%[Õz<Ü𢟳6vã3`u`ƒR¨%â0§`gkžÓöät#$&Uš†È„‰D( #$'Ëû«ÝöT+£RÚéÒçG¨êÓ(HzƧ`>(`óÓÜÚe#$7ú|güRpFäŽcÄy èRuCöÍ—0ÿ¯» †w*µÕåD̽žuD2#)ŠAù*†ÀÉ&~íêé1[™p’»I´ˆ€òsF¤Ž§ßƲj³Ñ¯¢w|‘jG®m!)±„gοw×Íi(Œ2óøhêN>ñÎvq­ Á8XAEçÙÉèbG—Ç_‡ k’0K}´ªñúÃPˆ­`‘Š#)ž—1!ç@ŸÅ9À"&~X<@Sàä…“Ïé·Õ*c*&ûþ0û`€v€Á¤&Bmv>íÂíôwçõrÃJÃÛ^Õ2Åaðžhk_Ãú§íùÓ Òµþ’àØ6ì×?¦X>á24",Œ‘Ï}¤r$nZ9hNz?Lî›+e÷ég¯ÁâÝ”×xþ«øwØjS@¹™aª”ªç^EÈC6QŸåk]߀Ŭælôhå}¹g§MSøÇëáf²2|ü5?…‰Óùy\á(=cŠo.z˜:ë©1g›ëûõ¯Íïyiœ=›ð`>#%9å•Èe5­~¨úº¶¯"ˆ@ñŠ'àaŠBÉ’Ðì€ãêÑK¦$´! @`£‹”±ˆ|C‡ûWeȽED=nª©ýKnŠ{Ù(Á´É%š¸yŠ²7|IàÇ÷qþC9UWÌ<µ N×VM…ï6ÅÉQ$QU@›yhŒéʺ¡¿º‡fžÊ½ä‡å½’&ÊëϵŠ6ØŒž<˜à¿lûœá™¼“J°ŒéôZþ#ÏÞþìiq¦´-8ûǬrÝ“[c|JÉ*ÚILw]ê¡#ô}#?ÑÇMu†¤‘µwžç´Q {d]=K±þ¯ë¹hQl""^ûìŒa'ëïãîW3 Ÿ/w«3"ðªAGHQ:Üþ=˘Iéïy'É0&½i³¸påm­`zl6}b©Œý³Æ,-˜}t÷y]¤_õaôÕðèÐÕ†#)g…â~dÿ•¦Ã¨úBbb/˜ÚØ ¤­Ï›µé·ó¼žäi°Œ4öçëÁ½˜GyÈ@o³ÑÇ«„0­¡‡û²AMObô(ñm9N.Ö5Niݬçsc‡ïÌ“—úo[´ôúœÂ½ÌÏ¿—@~÷LJ&hŽ”Ñ3òÓ;³Ì#)JK6åô]ô°£7›²¨y_nP[L§ Ã0ÏXI#%æ°(“ÊTŽ‚ò#$KšwV~byþýú__>ò·”ÿ„<(Ù ¥¢y¢™ [ˈïÅÝ;´Ã`D!õŸ§ßuy&?;¼y›sŠoz`×ñ)Ô°;²ïrÃI}x—²ð¯uñ;ÿ^ù#%#%®š¥U4Ñ­#u-QF±•OX}ãñÞ–xí)ä0ëÊqZŠ÷û–˜A;â#PßC"S …o|d‹c±¼´ÞY$j±©4mÓPä¡Eù¾¯sp¾ÅqMYaÄìÙÌ;ÞÇŽÑüšÚ‡ºD’n;{ûnSÀ‘Ê;Õv’…¸ï l­ÇM#°ëù—Æ<}€zh5…ðþ¨q›Jõ;[I¬Cc§|}x‡UFütãé¦íé~Ÿ9#)9]Á´™„ Ï·_T¼‹…c§ñ¾Ü+kèmi¥¾n@±ÙÉÒ‚LU°íî¸õ¬CÔgüqÎ&öw¨vçz5ˆêd!¤MÉœp°•¾~#9ç#)"hÈö )¨Íãhqd.å1!˜·RÆÙÃ|ZÈ*ÄCW—¹ñ¼½#Þ¤Åo}žžg#%(c“þÑÆ®gœÏµÔhwØ¿â– L¸ÇeÎlEw‰*1"("*ÊM™ô7ƸޱØb²#)lÛãüá#$cTÃÙГ^ÝÔ vx§RÇýÄs‘V*-æp#%*Àºìa7£Ê!¥^Æl¯*Q0 "Ô4Ø}Ÿ¿Sê=ú×ÃàR³Ü÷ê¼Mõ×ß} éEO £µ¨!Äsü+ªyÐAû–µhˆéY¨Ò’yv|ŽÚÝÂþŸ–ĘÙϳ‚:ø ‡c¥›u]ˆe7g}&Õ¸…î^¬vÑœ_®"#%œýÖºs#%Êι}Ùi‘©¯ã½ƒh^ßÂìjmu¤PP;ÅGY¬‰—Ш ¡ö²Œ™cXV¾J’(‹0V“‹-ÎRprÁY²²&j³F²*)™5$.ÜÌëwéd2óÈX㊅Bå|ŸÂ8=Ö ³N®Î‘÷^æ-tu ŒâÛÊ5Š#%¸ég³Ã¾p˜|#ƒNF"ç/P”ÃòÖ±(z¬¸Õ>:¾—#)Ÿ•ð‰Ëøë±ìùÎÇZõ¾ŒnÏ7!#ˆÇ.ŽŒ;•±·£G;ksc³änžž~çöÁÝ-ºâm2Mä½Ý\*OLU—6ú²‹qÅa¦¨ˆòê †v²#$Ê#%š$À uxê"’ÕužòŸàCè–}ó9©Ï¹àÏCû¯ãKM½„.ÿ*ÒÕ}¿cÓ{9ö(÷åù’îAþh5³Ý}IŒœqalá|QduD+661Q\WWwž.מ9ˆ±ÍÑi’’ "‚Ú;_—¦uÓQ½Ÿ†õž¼ŸvpøéÌãðy!d ¤¦EU#%…"²ÌUa˜ cQ:Ɔ-zá‹oØTÈÆ cŒŒ0Hl±NÄ2p°jîŒØ§§>YÛm*vmše©U7`³JoÍJ}*28ô”RrIû¦tXìv/j¨£î—`ܪ#e§f†hMXþRIB⃱‡0,®Pã{W”t#%Ö77m—”Ýß©²îÇ(uôåêXubM„`NÍkû¥ –@¦È‹“’€`V™)¼ôå¤ÑmÇ1 ðaÝ!aHŸ#ÊãϵÆLëm_ ²>aŒšEoõI½ªquÛöˆý}',»3†y_ÑdîÞyÈÓµs½ôÖÌ™¯>ße«8~±…ZFçD·`¡<}4DqKXÞ§÷jbhÐãA¬ÃRbeç}"hC’æë6Ç[1KC”›Šb¦¡H ¥äe8ÆìU@SFäÃÃ3G|ì×C´.Ó7_gžµê¼[•+žÑ þ xÌ#$CU¡T«R4X^ØlT£ x”`aBÇ—£œw÷ýo¢?M¯]ùó Hl’R±H¢6… /QjûÊóç­¹¬S"ŒT©¨«à›nhÛzÔ[è©çzÑ“%,HŒFØ£U†6Ä@‚°ˆ²+–ÊÎû«.·›#nwØ-³ÛÑÙ´ÇEÆ>³{§aûŸfÏŒsF·^ËìÎ}71ÄŠŠûVon‰—á×î¯gœõÙ½KõzfmÁÜè}c¶iPÁ;EÜùû“ó£UÚ“^º@ÒáúEŽxÐ=Ú-„#%+Û· ‡ë·©¢ü”‰:»—%˜Èfùf<]åæ¶ãVC»I:~ü,Èû°+>ò¦Õ芘s=¶¶ƒ Ë–f62±WS+DVFÆ”–ƒrA»k&#%}+ž_¦%Ãk&܉‘¾Ì‘®ÕF4TàÚÆûr-nøßžÞÉÙ@>­SôV¸ÆÅéA³ê8z-Åj l!³ù`3XøíãË”±õ¾«ŒçàûáÎÄJ¦_sÞª˜ÂtÔËÉ8ÙïµÔŒajO#%@Ô™¶¥„ÎÏIü,ç#—úú¼tÛ#%ý²ïç£÷_À_¯CYžó) AÄ×GKh¸Ù¦’•ÌP"é“ŒgðÓÝ-Ƶ ¶×iDzÕA¦æƒ Ð^'v7‘³æ¹1‡c'¦ þ!Î?¯L<1ºmË1ç³ÏWëW)ñ²²Öˆö6¨Œ#$Å<ÑöšØbR"¢h³º@k,± nu¡\ÁéÅ*¥r%RRkŠMßÙW §Íâ¥Ø7>×Úòø^”*â÷Y°Á(&B"¼ó<9«"+ ç+ÍÌA¢¦”Ò0Y% ‹24¥!¡8°‹|:÷ryvëoŸMQ»ly7°Û#%<âA7ŠÀ±œ2A¢„Ò¨o.¸…Mh©4iX,4·AšM¦hyš £MF¦¬(iã0Ñ ÄØ!ˆbÓK³#%ý{Îú '„DbšCx2¹Ô°Pâ²V9£ñQIŸn5U念ƾòé‰\£Hñ9¢]§—ÉøMƒ¢ÍfîîŒï å#j¹Ë}zÄ®0äN¤JQ0.ˆò¸á§#)f§ð#Æ©”cø}¯T̆ìw‰ÜL†ëd²Ù#%\×+6 6+²êÍflöfþÝü¡ƒf; M;Ã&òcÑÊo9Q¯àn@°mN'<à ÷wçפùâÍ"Ê#%_SF3Z«¤Ž­§ÂÿmËÁÓ %&DüªªSPÉMسšÞ)61‡ÊDÔÛø}æ#)6^ÒéSäÃ,lûEíë«5Çìñ“ñgÊq­ß2¥Aüí+/÷;k¿\~&Ã1¼m5ÉÉâ3‹³eäÿÍŸ«· 2c@˜4ó&ÃAN5“ÞàØÍ› Oð·rg¸?Zùuf/œçw#$ý„kª’m3¨ÊBvRRkµÒêQR®%!©íõ^RôÙöoα¶éÈÄ㓦4±Æ²¥$@-ís‰~#æ»­½Ë€>îÍx3›µ–(Â&†’èÍ·.L«˜Óca5"2i¿OI_\*·÷‹˜q}/l°Ñ#)¨Dë‚sÙˆ7ôk·Ÿ ÏR%O>FÂU=Ø£DüqïÀ*l?óæ#%¢ r/y®vïø¹ß÷r=¶*±‰ýUm´ˆªT2«>ՆبKñÌåœÑK«¡`Iµ®­\Žh!ÕÚ÷-!°çS&Hô ºX€¢%FÜ«\©"×6æ¨Öæ’ Á#% $Cç*LDŒ1ƒ²Ù¼» A w ó¢D˜0‰C%ŸÍš“§ç3kP q 'O¼.R†¯ ^ŸÂÇì;¦ÙˆÔ:=AIë=g¤&^`ûP€žÿ_QðUŒ3–ƒ°ï̼9Wxð-¶ßFà…òTâ¾·®Ÿ[W¶äã@Ì+Ò¥¯¨pÉ9j 1œÌ+èöéýÚGä4õîÿfÞ^„lTbƒç)YO,ÏTǤëÙ7 —|5y?-·ûµŒ¶Æ‡?žçr?C>€fú'°`À«eŒ@k(ã|‰ÁT>÷ŒLþY….?²­æ3ð?Ã^jþ[Æ[]¥)WbR#)²¦VÔ'è¤L¥Â't¶t¾¸wÖ­»·ç#%—JÀ¤rè^’ê ÙôC‡GÃ`È$[…°ö#%; îꦙ% ÛªéåáeŸ};9y_ô뮎nÌ>z#$×ã^hk>50ïø¶êÖŸ_Û«ÑUZî®#%o‡<XDa„F³×¡tÖ1&.HGk$‘å<›jyø.RxšY`ý(²¸ëô|׈#)Rj e™sHsRKŠ0 ³@Æ9‡þh‹ÆAõ{>ünú¿œ×·Ô`ÓöIAÅa\óÏj¯%â–Û³Š¶£a–6Ý°¶#ù?³¯ê#$Ú(æ#$KÚïãjÂ5&ŸWëü½¹ø€&zê$I#_±Ë]ßλÁöi[=‰Õ£_0QÀD`G”¦×r~‰“Ûb,*[Ÿµ ŠfÔ–OÄ®dÄl©bQ~‡‰£MŠ¿¤_mÚŠ0Áª”SDB¢ZHñ÷;b[Zþ€ý&ËwúÍÛ0öÿ1Ô?À"ôÙç#%¦ÚžVÐœ tì•"»#$þÄ,L ³î¾¶hÅlX¢”U@ÐA¨>AjHvÕå¤'g»ã_#%ߎ'ª{e`¢Ê%£AÉãþ*”’‡Ï§ßýKÌí¬#”J8äblÁH)ø¥0?Üa$¦ïp7ùéMh”Í2 »À¦rdZbÔ›^M¸†Ûʶ¾¬ª¼š#$b`ªª¨ˆ’´aùfãür}é‡Òí©WU®“)Œ"#%´¡Ø¤ ñ_à>Èé•þ¥mpQ¨+™/oîrWKCðµ`ùFY”þýX±|°=gö´º˜`ë ‚ f˜fJ®“ïÇúç«ì4…Ú³Q3ò¯Ð¾ÔÊ礞;Îãq´„ñTr…@JE\`¤)³Qå]¡¸€È.²wGUbw: Ý¥‚Ðr!&˜_Q?µÃCAêñӗι<_‚>(œ¨Pˆ# ì¹4Þ°È¢¡Q,;x…#ô—P=|î˜5_¤ÃðhÌöýìYC (ôÚ¡ù˜~-¤7vtªÂB‰ýU&‚ b$þ¦ÐøïÓüû¹h ýVRßì‡ñ#½qÝæý“lýÞߌütþß‹¿¤cçŸX>ï“Þ ÿ|9]äÜŽ…Ü *8yòÞ3F¿Ü>šö²C%^ ôð<Žú—7Ž0 ;ˆùcIý§?¼`‰šSH±ªÉpÞaº—}²W‡ßWG#$.œ[…CÊ.ȃ]‹ƒ­oÍ_²‰Ó8O}Áˆàdó5¯·*׿÷AFøéåãY­+BÇ]Gãö‰å_Ó»~ý‹ë+þ=Ý‘Ù[i’wåéýrPi¯{…9˜ovθ$l§tFq¸›J©Üsªz¾;.H~ª¢ø1¦ wP]·©{Ô1Å”¿³­,>*4ÖßÓ #%‡-à@zô£‚½Ê÷bè6Z‚HRÞ­úNˆ»'9Ürw¯šºÆ–çqðp.Q£t™-¿ê3!Ö…!USܯ Ex4ËŒ]°ˆ•”ùˆýiXœHú@òœ†€÷ë{ÊnG#)ßB0T ïg#$\…ãƒj?Cg¡ã¨P2šD0*¥ëœð˜dÅÊ…àH*å,ê1Ð ‚“«;Ä…¥2˜it.0TV7E3'þÝÈœËU½¬©#$à„¿~ùv¤9±wÓëï®#ì±£ó8ÙöîÓõ@ý–u$}Kiðd ‰8)\GíêQñÓ@s”ÜÃœVA„7-Ä—"ÐB¼¨$ ÒGòÅF5|û*û&伄¿¬„$PÖIq.Î#ÍÙ‹jF• ú]ðgû?ÏüÕlÚ&;Àuùè{±ëLabýkÒëßGuÒ„ItV+?;´išh€Å ÂCc뙧¯Í ôÈu5´XA"µqÖsÊE€¬köÞÀU(&òÀ1#)Jó« pˆt± 4Ö<¯aüî&Ñ›²òIÕ#$VP”#)ü•<Ê.ÍËWTó«•¼i×X’¹pK®r;møÒƒ÷¢!T›ä.#$-?„…Ggˆ(,Ö¼ác#)K@¡’˜©·¢a"ˤ Þ)#$ˆæWÛ•eŽ(ÏCžˆQÎDAB2ÝÌ(‡k3.9ôÛü¹©ÕÖçý]óy>‹<~Ž]xr@ó õOßÇ5?>‘ó~ÊižýõxÛöÕï…”ùòÈ.x²ê¤gð¤h)mU×~3éoWˆsLIÝ‹èéüviÝn›ZÈãn\ÿd:ñìÓîžývMåyaå»nl$¢‹*{~ŸÏ„0ËQo!òêëá/ë×t2ªgSÃ~¼Þ9ÜšÝvÙtx«N—òÈSsŸòݺ_%ß³W†>SH¬}ׇ s/.Û>hÜàÙYp[…@MÝ>zßO-¸ù1²c«Ž^;­ÁãËË™(ýÉéþTzû¤ØB£Žsô|®Â#):³»›–üÙ¥F³U¼GÐôÛÇÒñÁMR¢9©ãÌäï¦.×W"ÝÍGNÙ6~L: ëÿÛ²w¹:gŸÊ鶵Œ¬®ðüto›ƒ¹þ¯§MNßÍzYËü¥QvSÍðpÊ%Ñß›>0¶—Ð2Ž‡þÜ€Ôu#?”Rî¸ÎeÚ,ålc—A”])l·~9- ×_mT袺ú…V¨ºêèaMõKMTÐ#:–#%™QŒf=ÁEbÇ<Q–±¹„\®ÔàÐy‘sÊ®j>¿C“/UXæ¡vøïÛnš{w×ݧW”èÐMr»O¹þQêô룯E·uÎŽMí†ÍYšÊrÓUoæXÈmß:òòÕ.^líI€ãßΧt•ˆ‘™ü7*¿ÜêŒuûó3ÆâF[åÈýQèù:ÅŽº9/¯ïx×ó„Î<¾>~UêûÍßÄ{ƒ ±ßlèˆù!¸êÎ1ˆ»«L*öÂЪù«¿#)oúqòf¶¦ëÈSG‹«Šº¥ðY˜|Ϙ`~½rè½™p¯ýCQ«ì•¢/wöýuë›MöÇž(—õ»Ô9°¤ý´÷ëªêqýTDŠhN6¯»àÙlÓîÿËùfÀz®Ž³w§ù{q‰>Z³h0Êûht ˜WrÍ´×/ÃÔÛòFÏÑñ8P3Ý\3w¬7{8v¯%ëaÅ À©'X!y-îý¾F´Q›­Ëè©ž=»ÚìÍüpiúøÈFš$.WõòúÙßÒk“€+û#)ï÷iÀÿ·ŸoߎIH·‹"¡Gu¢Ø{/¶\šç®uü^Ÿç:~}c“}öþ'ßòü¿S~÷æìËMVòC#$á7aéóÑ»Ç3}ÿ˜!’¡WSx5-ååîaëü?©‘>øÐØ’×¢‰TtvQWOâ>°µÃ{÷H¤»ægr@¨0¾!ú¾/c@È}o(>T‰Q@¢£ BZoe_¿@_Üóæèêç^ÜßG§m›yÏÇæöy;9ñèx,ͧîç6á,ó~s­>®wí`D¨£‘¯Ï›GÃ+?oÒi!sbÿ©zu9yéaúìä^Š9¿,Û…¿?G zGà6ŽaáÃÉÜ?xP·jº>#$;ïÏ¥#$QúŸÊä“ú‡hö~什ùÿoá3Çá+œ1¹56ºý´ …7‘' ÿßk…Nªøò-cÓ¿w°ù?M9'°~9„Ç_êññ fP>üÿ'Ví{@ø‡Ýô©::÷0EÞ¬ð£ÞW¡ì8)ùÈfh$54€ü¨B ¾ˆ#);^º:i#%B$ÂAPoæœu¼^2±ôôÿ¾n±´[«‡ÉúHñÛ/ab¿cpv#$÷×ÕÅsr;÷Qºš ’ûÏeÏÉB(÷‚;¿_Êê¢&H;“¢ø–ü×yhÙòé[º8#ó¿f#Cé[œOÓŒÄÖY#$(ÇaPœd%„Õ#ê`Â5e.CŸr’fH&Ú\Mw y+¸9© °T ñJ$s•#)Õ½uWœò_·<gLkˆoõØ`ìqÊ®[©ÝhÜeÔ¥Hˆç`Àg}:£pƒÔŽ}ß·t’º¨ša¾—š¶.Ç\E¢^š I,1Ç(Ö!œ „> «îÛŸW}¿·¤&¡ôêû…¾½ÝYã: ˜ër£S¶W=% TÊŽW¹ðœÃõÑ$¥G²‡lÑÜ8Ãm‘“k—ÕŽË;Þù½Ík/0ŒáówdÊâ´oay##)ê£)ËÇ÷æ¦{ÄtŽ‰è±ÒŽ!‚¹óË%ûÚEïisÂ<¢á‘¯;éK2yÐówz• DBIÑU^г«P$ý^ŠÌu¤ªÍ#%1ÊáEó¯#$Ú2A†Z»5šæ1÷].×pËËu=o½¢o<{ë·¦8þZüGbòí¤5úûþ ?$z‡"+Kˆ…Ä®‡Š í¢ ŽÕ%‰˜#b,7ù÷gå²#$‚´a量q¶ý§×LÍÜ¢iêÅã·FUB#%ÕT颔à#)}ìö}<Þõ9Ä*<÷Ç‘-‡Ã7K® _‚(çÏœ#)nÑ«ŽzzŒlž™ÿg[Ñv·/ÃëiN8t»~̪—E.‰äÄ™3?¨°=rØÍ®ÿ+âÙ½Ÿ6àãì¶h仳ÄimKå¼\Ç´g?‡›Kiaàw»Íü5®g¼¯ã¿ÐÏ÷ÝðÀj³…»<öøÆ5ôÖ¦IBitàÿeè}®ëƒ%XêÂ/u5:ºÝÇïtéô]l»?<Ô <âðÝ€(Ò5–写Ž$’_¾áv“øyæ*¯åü•%<s ˆíón»#$êDÉ*1ï—ËçÌŠL€§Œ4ÖØ#%%ñÐå7våóeù~Ÿ8#X#ÌÉó£ÜÅJŠÇÊç {ÞÎr–VÜ„¬g³,qz¤_™ã[.ê1í­Ñ†¶±’Â8‚= ©EJ$ݱ!±Ã"ÂAUZPq¸TEŽš•BÅfŽîSFe™ªŽ#)˜*KhfLvÁ¤Øi’hÀd,‰¡¡º¤ŽÆƒMIc+ŽF1Àvéš4ÒJTD0))'φq8&¦dÔfˆHÛì‹B{qZ8dÚu¡ýß²ø[Ñã!xµœ¡AAóýÜÆy,?ˆ—”b—ñþ¡­G%¨½tr§D,;Œ,"Œm&;Ž˜xrE+ðøgOoóÇÑdÈ#psuÇ…¤ËGGJf³v…¤Þ ÆA±aR4š„!¬÷â Òm7&²2¨þ¥@% IšN?²ôöË]žü}×W.Ñ>œ¬çû-莃eŒüøÝ;1ŠE\+QÊj«6hW*0åXnÓK#%ߤã³0üú¼·ˆö"®Ï¯ö÷aŽAùÝ»n¤t=5#%'7ãœYtFl]&®tüÕ˜ÅÎóõ,©ª“‘º©ÛæñÇ?êüÔ=ó”auRhô‰$v»æ.ïµ*Xíäó~ï½}òñ͟Ø¢V„y#)ýnç#$2#)4ª×õm`+¬çøq¿ê…[vrsã[—iʹNΧ{‘ÅÎs=ÊêÕ†¼½VÃøôvǺ?_çšGlƒº¿½pOlöÌóùø|_df_O7÷×Ë×É.ºþ´N‹jþ}™¼ô˜!âü#8°:ýåö«Šb~ž°vM'!Û ü¾Pg¿V„2biºÑäjÑ"¶”ÁÊØ¥;䧥‚£Z!¡”}íæ¼^èóÏ9·Œk»µÊT’"‚!R¶¸aa‰F$7d™eÇ Š" Û£Ó,E…®¡…yjÆaYlïí¨ÐòÓ•·­Q¨È)j•ª!l¨Z7UPÒ؈ÉAºv“y»žL‘wä%r(*`¥2 ÜÁ”ilÄA4‘ƒ‡}^uWm’šÆ1Ò!³,ȘaÆaYÚ¨´‡Ä§qÑÑe-®g´8o™Ã!kAeBŒj0€0®ñD³O†ökQF†Æ&,‘&T8Tâ$z°1ø¨­Ù¤È#ûš#)–´ª¬šŠ¹£Pgj•DaWzeF8×Y t! Ô ¥\3Hf Ü|ªÃU¦jJÙ4Ryµæ›·œáëP¾i¼ßCÈöð,j©ú¾?î:¸8N‘ö2ÎqÕ£ùgGeîù¾Dê=#že ãš®[ý¶„öûþ†»Þ^ëpˆV©…qáý”>û3"°ø?½®ŸØ7ù´ripú0š Áë”SÈ1H#ÆõèdÚs”R¤E“¼Òw,ƒžÏ+Îgáy«_œ¾rËÈÄDQ率½ZYèá/>ª3xú1îpT²¿;œHW_ü,_£LC¢#))ˆ§‹”#%áyHâHr3?aN² q;¡ùêçÑÆ…"[ ÝFšCžFÔЃ%øºË™rPUI$³*ªµ}*>¨$uXK€rT‘©ÃÇgX€p‡qÜnCôÿ…Üùƒ™÷üx”„ÂÝ#Ð$no$ãºÛùôáƒçïý^¹&'UUP!ÿ¥›7wN!ÞÜøúgÛô×ËÜk}·P];ŒÑÞ\çxÈ}¡¹z;:™¶^/ônKaŽ×¦•;üf’&‘h¯°SHÚ>=ûßm+Ñ#$Îêà@°PÜüò¯ãøÔ.%e2Ð?hÇPª‹X¿ßŒ6ŒVYJ%QFH‹ÔK÷±[#$Ÿé˦ðNpèìJ€‚¢²±D`ÔÕåŸgóù0öù2¯×Ê9o[PnÀK~äÓóèh>•µ¤(`*‘b0Dg¬×6úÄ•Z,D“ñqðà±…)“ C-6ÑÁ­ÚkC+E5‚àf-jkta–4A„By©Y¦=Zê÷QVƒ!Zƒ#EÇ3ƒˆ\˜e¡A¤T`ÄÊ+Q`|ïCÑÅ¥â^¸˜X‡ªk1äqVÕ£¸ÖaÕ#UŽ¾®CfÇ&ØV5hnþr2¢0Õ‚åS¹•1é°2ï2ðlˆ«DÎÏ€fd²À]€æš¨¯oVŸ\!­VÜcmÄ2ÓS¶¡Ó†Æ¤ÅöV.Ëc4ÓhÄpÊõ;Ò­£G‚í3»;Ct8Lm‰ÖÐë¶X6 ‡Šn¬ÈdëÁœ4¡TqêŸÇþ/ôìÙ°å¬3å+¦û 0×®ö\.»¡}÷{Ñw­ Q'¶AájÛóº)⺙p<¹àÂ)X,5¹-6åù~ÍOÛúŠ›l hNN~¿ÅÂüÝ€v7}ñŽ–š‚HÍ™( #$äTbP/·)“M‡î¶ýñÒSL+_ÈrAƒ<cžÿñåãB`FÁôîŸ!ú1µ‚ášsO?#%¨÷äÔ7¨ŸA½:¥VkôkäÕçÀs§8'œ°@ëpZYž #%ÁèââåAe`mŠ°ÅƒSø¡UD q¤{.ðÇ·YŸZ5èÀÀâG¢.×^ÎDTêvº‘rŒ‰7Sk0w뱪ޒ'h¨6ušÃñw†{wyìbÚ°ñÖ`Ó{x9 GcÈE¢—HZ-ÒÑñ¨ÁÐ܈¶ÝÌ#%Îm]*«EP²§-¼ÿuÖí ¬dôq!å&€P””f4½ C#$^~mÿú¼þç§ÙËùíá)O5Ô»—¯!,ØuŽÄ÷^;<?¿ºŠzi`CàŠqU÷aø°Ðñeã‰4.ZP)‹uF67eÈÇs„Êæ ² NŒ)‹ƒ!oóÈlj00Ç;1âê˜Hyƒ#Ü$mêª2©Ô";-Â|c®áÞË~åÝyÛÝyst3‘hÇ#N/J·^Ê,°4'×æwÙ˜q¤lÑ ÉÅ›pzE°Ì)Q’f’™PƒA­0ãÅ+#$hg]´`ž<f–j†‹´‚¬-Ý#%þf‘•s+|`ª—ÈÚUèwHn yv#%Ë«w•#£¤ƒ‡ ŒÀÓ¨1ŒÛOµ]Ú |7"'kÓKF¡§­4ßGŒ#%=‘ŒžÑ9N-î"±–Å`j0tª<ŠL²F4GL„¢“V¿²kYýÖEºSÙÉ*“ÆÁDE*Œ;˜~W䥾hÌ÷4Ýív«ÎbmÓ|‡…¶X[†xÒ­A7íGãúý¼S»a¹Ù_ÀœP^/§KϦ’¨J8Ÿqc‰”xù ÁÒÎäñhv-ªÞÂëŽÂ+ûZx†ßY«oMFä™L[Á#óµqg#$:á3m¶ƒçboÈF–L¸G’/s¸3w1–ÛsÀËʨEFj®:çdÍ+TºK.sKqŒz±µZ$ÂÕ¸d‰¶õªÚÎ…ÝšY^ئfÍ{—†Ýô^=õx°v Ã{๠‹ã³K°ˆÒ†‘ή“bKøV|Õ^Ú¡›Û†·uú[I÷r8ê›Y«#)P<N=¦,Ü9v‘$aö5£Š¨2áBµÝ&ÙØÜFÚäØv<žD?mÓƒc3š2Öä…Dx¹Ðpk‚ø¿+ÞÃ|ùðãZ’O:u=\¹Ö,í¦píœâ7ŒÞ×OŽ\ãÒ,aÁ¨!µSyŸ£°q2!Cng¨ Éœš#%!& &HHc½±e êÆÕšú>s¶µŒ†0Ð:l.Ý;æ^“wNU²tÅêUo òµUÞLªƒ ·q+,OÃwå]‚'u×!ºÜÞ7,·äµÅÅ8,‡T/wµ=cwrîsÆÓ^K·n•Êˆû¤´Ül7Bˆò‚ŽŒÃs ½Ç~véûmGQ×dð “y‰d­é÷8Ø,æòÙÝ6øi].£VÓ!‰#%0á™ZÅKŽ‹ö\EfÇhM=ŒStÕÖ1˜ÓaL¡¥ŠåšÄÌ qª]:lÄç‹' Ól± Zs‚ó‘Q‡ÍôrMðЄ¸ÅVJévöÀøV¡Œ9sŒ’$ÚŒ°œ¦ËÌÐåá÷{ŽUãr¯·F+"¦ÉëòÖ`Ô¥AÊÅ„A¯¡P\‰{†œmí/晋Àsç[D(ˆŠ‡#ÄEã?NöØ÷dce¥ñ×c¥zÍã^b0â11¢ë,J–»ex¯õç§0ðLã$‘uK‘?&TÖ¹Æú«x̼[¿ñH\M^í¶ ’Ÿ™œUC¿uJ7+—êê"Öq¸*·¸ø+›y›žÅ‘ ¡ˆ‚_#%•sqy|Õ¬‰çj—ƉÅÚB•Ä#%œJ_8yÉòÚ\Ð’T+ŒÓS”VÊ’<\G“æ¨ÀTíç¦Lšéç¡ÍèÒºqÛÃ87Œ²@×¼pC’™u:ÿßgïÛw¾âÑpGJ–:·h¼'z»èoÕuœš|„'­'" Ì=ˆ›–sX¬m™ý\Ïó£[±žÓ4µ^#%q™ÑËm´ØÿI«<dÁFÏrciéß«|øÚQ×G §|í8·-Ü1.öû[¥HtÞŠeÉÐúFD˜út¯p¤“NñøÓ¾öÂ]•È×yÃéš4Éw s¯;Ì8o,³)±ïºÂŒ\25ùX·çómf½Ô#)Ï ·>LìÎdÂ1E×Á‡#%¦Žn£",U‡±íé]ÞTnöò(öóÞL‡Ãp«^ü^VŒ"Rò½¹7”$Sm­aªØ%1…#%QÜì1Gqøì:BŠE,í@ÂÆæ¡TE.¸‚^Û5k¨Üsvª{³¦²v=ã)ûŠÑ‡u¥ ÄéuØ55…,èÈÆRÀ9œ•F†–rü=‹Î;~v ŽBý×pq½grÅu¿¬¿ì/ŸñÍ c¢Ce?q©·“Éêi+°·ìJ²\<>Ó©T ,W–8¼þ(÷å§9Š(åìsµ,€âdñúaËÛç}@î{(aH¯Ô®¿ÑØZV¨ìÁÇG{æ®uů=çÉ­Û²f|¶x@Xè5èÉMUM›yp jqpdÇ] wsE"è/JœÁ<š®a„Ú2\Ìaˆ¶Ñ–|þ<;˜JG®ãÚ%u½bs  ˆQu嘅A[^I`QÙGï¶Î±›ùæÑM©eÁB…M‚vêp|A`£8#$@uæˆâº %L[šÄ|µ• Vµ Ãvø4-¹%}ƒŽÔU…=RÖ Ãy£”9y¢"0Õ€uß9m¯kxuËlÜ“Òi†NË}¤;̲Ùùéà¾8WyÅØÉŠS¤#%gY©±Q…ĽâÙ®LëÙérFtTUÎ]Uÿɨp2pñPó·EMç³u#):ð8Á2f*Ÿ)×Ï(~Ë<#%‘JJ·d²àŒ½ÈP‚¦ V¼Å6ì\&Ž3«Ñ®Ø$d[p”P{>cçãågcÉô³ë»ÃÎIˆ—îÁ ¶œ`æ(x;å?‘,V×Xñš‡v«é4„*HTÈ0,¢V#Ö.ͱ°§·Z‰îtÜ8,f½‚"ç&c‡TÚì ì òº:ìû(ÉÝRO¨´Z[wqi½?»‚£Öå»5äù(5Duo='v土3î‹$|8é:¥ÝD^¡{c(›„B$ƒœ@w½X¬ÌðÖMª<º&LF´+Ó¥qÆþAX‹‚zj½Y±G`qaÝÐî„™‚!ÆÆ ˜ïÎùÛ=tljÚFâÜKÇ™ƒ‰á‹ëòóîŽ%û‰éú"–M<¸3CŸ9ŒpÙ ^‹MdŸâž™gT>"†èîæ‚nÍ›·_j)|%ÒÇz6¸55øcOrúùt6Ï¡E'¦ÁŒC#$Œ·Q}Kj£ˆ+“Ÿb×\ÈÿË×àç›ÑCîÉ2ènCà‚Ç ü¢q´ð ü¹Ÿ|“!í¬7,š¢ËC8í‹PR”Ö†rK¿Óp2óÖùàx’Ñi‡C§ËSZÇŽ}ùovÝzhè¯B(†%·zæø5†1ÿ%Jg-†úš-¾…çˆÌµ´.Èòš¢žPä=Ž¿¿rÎÙ/ôr(óF4…º™åè~^ëoÔ&)9¥5 Áá‚x lÛŒEcÝ=\qá¹i^¼Pôh\Ý%µB[o^Ì–{-ÎõâbøDQ'êÃôc¯0[´ö]Á¾Ìž!¦7:|¨¢¾)ˆÇÀ×'Šá˜0c¢:wÞ›$ 3`˜£Å(…ºwÄW·&”ªdo¾?Ñ#éÇÕ¸»''/Ðôvðšøw.ÞaUN®—Žçä&ÏÌÔ}åÄo·¯qëª]Sñ†&^¦ýVWªQöc°0;·éqÃ}ž‘¬[šü'ZÔ#%#$½ÿסßRU¿ËÑó^T¢øô부Íi£Ù—Q4;·XrvH#´ÁˆŠÖFšýœç;ÇÌ|/<˜òë¦*„ ¶#$ø!ä|¥SŠ¦_ž¥;AÝeέ½ñuBñÑPi¸½\ª½°mEs=Æ"ë˜Ï£05ÂqŒÔ >"T³ÃÅõ´·Â×&‡Ë‡uiÚéé_œ}±ˆû(›ï:Á/¢LÖfž­.8r‰ˈÄ<2GÒ­?¿-¡7Ui«ƒhÓŒý[sú¦§w(ð¸±ç0s‡ÂÛò³×w%ÿ¡Ø¹vËê!Nle¾lÛ©j³¾;ÖO&²…R±“p)E¡ip…šdIÓXËm˜É×׳ç›—nþ$UÚED3 |;§m™yË{‘pD÷ó!«‹%xÛŸE«º5Â:oç'C†ÖÔ^!6x–ôŸr¹IöÛ¬6úC¿“¶s›ž™쥘#%.\¡"¦a¶ëÐA:M„=š=ɵ·86<kA(·77ž«©ŠîлOåÝÌk>^ÑçV¹óä¦ãWˆ7\u~¸ñeìî"Ë’ytÕŒþ…çÜ£ì˜ÎŒwÏ/W»ÌR†–Íúï²0Û>Æ™¶Ãµ®Qñv^mù‘F¬wzW¬ûXlY¦Ö’}E)} xÛ ˜læ0B;&? ËãS¦ÕI;±f¯c~—{l×c#%È?zQb…æɱ³„°¹ÁšõRLèد˜{ÆmS”+yÔ/<ÝVI×ÍÓÎÚôoäœöÛ#%°‘ãdÛ<Øk%HóÈ…pñÎó/À¼ÃöâÈx²ÄÄ.äè…·O§0ØõA†¹2a9ãjLÒ0<WíàÅ%Îþêý#%ëQ£TÙè¤sDS‰ø‡”¾-¨KìF3ld´Y¶Ù±¶õ6Þ·{HçìnÑV”ÙHN=½EÅEE4p;9Ý4øoO™$—¢?—ì;ßnâ]HëÎÓ£s¬õå-v˜}fe¢Ô" Õy,ØOãËÏR½±©ëNK'I<"ëΚC–¶OØ<UF)¾ª¨ðÛöÁI>…GM¢íÔ³# [mñ´9yb7µºþGôÙ‘]!gëvçŽÞ..élvŸ²pË”<ýÚõ\~NKa< W.FåyýUEæL׳œÙ∙łƉdî‰N‚:§'—gCEÜÎ=åóðÙã5¬è4ÏD1¡ßÓ@P`Î<ÞÄbf÷ ñà= olÙTgœéᩱ+¶Ó= $TÂø3Ñé.°›»´ý,©,jÉ®è"š{æÀºAr*„q‡Æ£»ïçs¥*”S9À ©ÏÿˆÓ{ÞBk•f«é¢¥æÇ­d(6•‹¦#( šKÍŒÝã…ó]6ÑU@Ò¤Á+\[ªq Æ;+Œ,­]š8ý³A•vÔ\¦wýÁ™Â¡ª­”#)eØ'p.z½‹,Ò¹ŽûÄ!˜¬ŒE.nMõ5j^Ö¸4K‡ì¤:œï’°¸¸KµëÑ™uP›Ù(ÿWï¥4Ó›Hv4зp~ü´‹a0†jªH¯­×©zìuòg Ê*MY¢]»,"á„h2[j `hŽ½æL,®s>:õ‰¹•[“t—<ï ‚±ÇæùëË¿»¼ž#%Ïpîù~8ú»yó¹™íã^.zaƒ‰’,s6¾Û¥|gí­ss†Z”iªºjÝk‹Åk¥ÔÒ/¹Ó–r¬/kDTsò>¹.Ðý– ¥–Ô7:Ü#X\Mt¼µ™Ê½æ4N”;U\œæ·¼õ·©&²Á˜hÜÖ;5ù†NtÖ8Á‚×[#ÜĹů.!Å)› ðûe·–øˆg¶ªäÚ¢ò"4=ß#%g¾vç>5SAöQ%#)Oƒ¹Îzù>ú½aNwÏ?âßúL þJ¨¥­”乬†úž²Ã70“êT¦sE3Ú wf¤š“%Z£¬ÔE›ø iˆ¥×Q¬I@u±|ž%hyogr¾{»uFr‹#)Á:€È W¦»DDØ,D¶åQ|[1eg#)V*º"÷ U^q˜ýÛù=<Š;Ùݵ‘öT.v|*a~96ÊT½ð­Å›¤ÁåÌsGD^ËÈM²Ön.ûî•Oº%1"]œ÷kµÓQ†O¾/ùp–0 Õ"Š¼m˜iø<£ˆyWã’µ$e¦KjuJ]^¢#3Ì›ã›6K#)¿Æ}Vgwyç—¿ofÉã½ï"Y¯š‰üª¼½üY? k‰—…à Ž Š›ÁÑv'ÓyÎÎÙüÔQÌæeÁ2D¢¡ÆœVŠ¬9­¦¸YËs€®H¥À²öëÂnÄ٠ͺJÒµ™ÇÐ'Âët74,®u/KsÑ#%½Fu'*÷hñXM„¸ÄÚ`ÿm7ç fÙÊ·ê#ma\:žÜhtÍ·|JÂDZ˲¶{„·¨`'½LŠÔ4Y5À-æS«mz¢…aiªÍ…Q`"tEþ¾°¿’ƒyÉÅQÙëãåyç5d‹K>sª÷í¬}›À$ˆ]6›Š[¶ÆnƒU[œEEîÇ93“BMiÆ>ìÔ㮸Þaq8§MN*Ç’‡&#%¼o}É(Ú<ÖÊ{ê!s«çÓW‹7Ӵ篮óÌkß$<?Mö/.v‡kgzÝq1¾¸ñ™¯dÚ-|©ÿfÇ—8®Ô:[{·¹ó®NnÇâòaæ*버Ë,ª‰P$±Ïž:oÙ”©UÙhõÛDÑȲÖL$IˆJwè¾N44†âêmðÜùmÎÐ'A½Ê¤ŠM_#)k>>=lÿ.»ãaÖ·'½dÕ•VÍðÉm‚0;#%vf|­ð©ZœûvŠ%…ÂàÂ{í|QÎ#ýö™.¯õ-§&?§Ï,›&—²>{œÕÕ;(ž‡så"Z-K“Œ-"èâÂúV“ºÅŸ0¡?zÁ¬Ý™¨êðÎZù%€ÓtÒµ|gKŒµÛ‹b2¡ËŒkÓ]9œ1ˆ}¶â‹ h$sÑCÄ#gj;i…%"ˈ,AzìÒ±,ûcÓMË„ñ•„¥$ °bn‡f{Û¬rïÔú:=6÷8áÄÐíCY˜¸ÅqÙ tɵÝ7 ¡˜y`Ϙf¢WêuuDU¶çŒó„šØN°ÊŠÚpôž%œ ÑiiÐîHµµ0Ó,ò}Y® æ›Ã†Â³u¶ó£(ç•#%3ÁÒÛm¦Ç»XÎϪõ±]Š¡J.Ì·›¡}ò}‹Î:u ‡S¼·™h#%“RÊç¹Á\ÃCÜ(·t\(…a<õg‚gì¤1šú”¨`:STÀKV!¢2Ê,²¾nhږ󪓖[ÔUøØÁ)ˆ^ŒTA<õ_×]ìçûïöè9üðµËÒûÔrö«Ùüî«Ãš––êîºÆ†ôs>3¸øÕÖ‡Ùå7¥\–ØvPòçñ½RGâ.åÛ¬ZWŒÙåN™<_ÕD 8 †zò¥î}B3é›ã®÷øÉ?%„R\}¤Ov¯z7æYÞåK»VÎÚºçT¿SÒÀ'EýptCd/ýL78—ç#%éœao¶õQtǺkút-u¯Wz°ÖÕ+­Øç:«8,j"#)mP³û"³ÝQ‰÷zÇ÷ŒÊp§j˜Ï0DÈ꺸àµw‰"±PÇé§!ŸÛß-7ìúQ§®"!0ÿm“Ãï~tÑ.¶3¾|›ki÷Î(Ͼ6àšÆ6ãâw³9N7÷00Žrœé~…W(•#$IoÀÚõ}¶™°Äå›&©J!J_OV­°½k†Ñö|Õqâ5Rý`´½DJUoë²°õ/*'¾%tóó³ÅçÌõÞE“º§ÌUG ¬»ðéòÎ<–FB­hhƒŽz½ÂØ­J™ÔEsNçrEÕã½Æ(H\I•ÎÓJŒôØ$ྑÏÁë„tQŒ¤zdóºe(/_e<ƒ7T#%úmÀí¶1‚¡/g÷›öj“;jRÔlÛ_ë¬ui¶‘i¨GB‡DS™ÚÞ¢çÂta-MMK 46ðÙN·ïŠ“)NnK µq…îÑÐÓ0Có®‘ØDD„1Ôùé~§PPËÐB¼¸#%r„oÉmiÀ+Å»EîEu˜NÊ•tGž;!|·˜¼h~W R b¢Þán‰)Š¦ëÚÄ™‡êFGÊEs\©T[…-Y!á@¤§aKNól©äuäˆRçóup|SaOdTj~Û\Ô¨˜¹›P‚ÓÊÚ.¹Ühßeu¶ÃZ.ç5]KÐÿØHv?õMùHf#$*˜IE#XÆ]qRªƒ­|)=Ej¥ÍZÛêÔsb?«Ïo%åƒ#)ÈwÊšœ#'QB4²®ù+NUB+–ü¼Æ™ .«xÍH¢›À—]s£ë£ókju5 ©›úŸ<çYç NƒF<¼¶ABñVÐ9´Ò§lÛk#$á´á%™{¥`9§Â§#)gBn¾€¬¼¥Êö÷ÈrÛ8UA”=ô D´DÀnpnQq{,0Xš£›ÂŠgYÛ3ч\ïäî{Ðë^‰Êòé=ù±@uï#%½™ß=¦÷râbhª¤¹²ÛýÇ÷óÛ8âÃêDsQSסϷ¿Ñøê%eþ åG;¯>nðþ]©ø­ÇÇytj]&uœ¥_ˆ­AÞ­jÝö_ê…~=`0: U/]’x8±Q®W­.øßµ„ºñhrÀ¶fp×z˜BÅϾ\£ß_/FµêËç²#oäqý´æ—Ã~9Ø¥øuæt%”´W±åvžê>"÷›eÒÍDTPG;‡¤öO›ÏÍ"wáG«ZüŽ'Òë}©³ø‘sï¨X…ÕR)åØ3ëüÅ:-#$œ™pW6C¿¡ÏD%#)sØÊÑÁÇw0ÈÍdT¯Æç$Sç†!’¢ óVáw:R—ËÚ 0JŠ#$ÁjÁ#%-RWH•hœ‡4Eb9³@tBžWg(é#$V¥T$¶•ã0¯ ‹heÅÖö::„UŽm#%I£T:ëj˹K»«·ˆõG!R9…*„À¡œši£“-ÊcTE!âôØ“Çݺë©NÆ!Ú¡pu¡\%¯2猶Ì|ƒ5=ø\Ô¥e¡€¤ ¬¹ÈžÞ²Œ+z-ŠQF#a%pð•M³GQW0upómV¡UhU×Ô„Œªçl}õ¤jÒ_"‹…}òñnŸå.{Á¶Ÿ×.}¾1<Y3×WÑwß*ûŸ¯<óºêløHš*{¯†rè.—±£tŠrœ˜¹¬5‘a¼Í%–'w©6SNQáÇFؽ¤m!Ñä¾’óÁ§Zn¼o×ÏS^~dc”wï4¤3"b)ÛúÑ=¨,Ãê®»j0&uû¨ºï^.ÓŽóñßPd—úmñÇ–,áøà›ßÅOŒærF^Ò&£Ås“6¦érÒ§Hp/@ia“GÆŠ¹Unh `RþÙ½‰±×«E Ô½ˆÞ´à©Ç]#% ó ”¬§ƒ¬šÒ¯Al ÂeGiÛ¸<³Õþ¸:©¦qãÖ'‡nn£vtŒâxªxÌQôõtcšÀj)¢mˆM»v U½ôš)ƒ`Öu#ŸG~û¿È’κ¹»‡€*Ç Ÿ£ ýWI¦ªKÈfQD‚T«X´wÂõöKnqBdn„¶ç²è@êÅëFOÁE”IîµgEÁÔTº4®vˆÎGÔ¼aR±Ùá¦è©5Oèů7=G¹2Ç“#)2ºVº =Ìè+”ha#)0fÏ—aD3bíVDE¡ÑkmðM~¾ŸœãW³Ž|QÄyÀm. ”‘ºQ#R¿VÕKÆBµ2*‰¡âì¶ÂÝ£Þ´] òë~ŠÏ“ÉÌøNo5))oçæwˆtàètp?Äú{ãÈÜÇ:ù[ˆï‘âÜè K—õE”Ÿ«ü,ž-zMªÙˆòò5teA#Ý@i÷?3齑+^Ý윒åTŽQb\#%`ñ€¿‘l6èfœÛ Çá„ôŽGóC¦Kâ6bÔR|˜>h‘°ÉÄÛ n–c°¶té‰~Ý&¡É^;’tÎû9¥•Œ²s];îI‰«WyžÿÑ6c”çh…ÃýŽ9èrjiÜ)á¥v¶¶#)Zd8^<ØÍW‚Ñ6%³œSø «rU»‚™“RÍ'šãÃðÞÜ@°xWºNLMü:žHíé¿nÕè€Û–9úyœ,&1¢¡@ùézötÅŒÎÞTðøWŸ#)5ÆŠYmA`#$uìLž@±†ôÖ-ÔM{ Ä£ èþýUÓ£•€P=Ùú²©j§U<¤뀩‹‹˜+fÁÕ‘›ÙÚùk^q¯§8x ™Á_MTVüô=ã#$'l)ó*Šœ±[õ=Ú|¶DgÉ=¡°t;}Ú2Ùx&ÆV´7ArøRz6{¬Aëô®Õ¥ž?µ Ö áÓLµŽ}#%€¹FËg<æRïf„îgÆ—ML:ñS:æ‚ A"¢çN¡[LJûQQÞ+J¤ðî@8Åz©ü“ö;bffüýžîX‰u42z+‚&KRF”ã?Ôæ}Ä–!\زVVQ¨{ž½£*y#$˜L!Q áceµpJpiM%Úx'¯}ßòoP§¢ƒºjO§b<cÄlÌËÄ,½>fä_({²©|ûÜä€@Ú»çóWWáÝÞžHŸ‹wMtl”¶¯Ní¥#$ #)ùHOIÁ? ~€{¹QŸG ëÞ>ìæómDû& MMËú|n¯B“{Â)Ó þnŸ§þF9–—ãl­…Ó%ýçdPûÿ¼¨qF ܶ$´ŸoZWñôTJERŠÂ,") ¡Eg¯0ñ`r1"B~œ¸^t?·äçdÂeÃ[Ò±ÞLyl'Ý|Ôøy•á!E`)$$ ¡ì!¦xXáØQÀÐ÷á™C*2¸ü|LG•;UñlõjÑúÓ͙ژlØ>Žé+ra*÷6²ˆ#8_ŽÎЬ( V>Otxadˆ—ì­³ 3n±Ç¦n¸‚B¨¡Y‚¶žÄø#$J*”C’B£IQ¹]éžßéâr=ÚJ(xþ5ãséÏ_õ2Þÿ•@/=tú×JÔÍh­(÷ ·Þ$ËCÀ¸ÀÁèO»‚¬oe.F!QáQxm\ûG#m@Ì«¢·Ò>#$ÙµëÀj?›#)þÿO¯ån—B¥<Ë´˜fÈj… ÀÒ’çåIé]Rz`àâ–*€*_‡ÉrGù©”ǼüÛ™Ás#)EVƒ¹E|vÛ€÷ã&Dý¯Ú¦‡÷TC„ºI¯W#${-þ7 ;Š?!cæsŸÙ¿Ã¡î4œß]p¬Ð!T¬Ëp˜Lû­Ùó¯Õ£âû‰W3Áq*ЃtT•¢T²;Ÿ[•#$+~áª;òÔQüa¥… nzŒÅmµ«Yn˜Š9–¢¦‹0Ïþtw:Dzl¥€F­EýñžÍ)Ѐé2P±Œ(áE5@röÓº„rˆ}³Ûì…áQ#%–÷Y#»çþ›k‚‡IDEOFÊ;çlC¬…²‹L|¢Èa_+™¨->,¤#%ÎÃ$È“ƒ’jPmHÎt‰@íа¥Ä*i¢Pz¢ìóPñD2€lý<õïÚbÛkî'ó@*‘¢úŽ ×†ÛC*¨o¹{@ð«îqÉ=¡ećÇÁíŒ#ªcxù¨_#$¯ái¦»ˆaÑ$l`R&5¹z'Ù![H‹á¶÷žžèrQš2ý8Y&ßçvˆçÄC³i˜‘ETž£¼ÈR#òdü^Ÿ#)`!±lœ÷™•˜ÒqÒÂÃìU¿,;iùUÎÿ•á›µ ’éÒYßò·ƒãv÷ÕŸµÝ‹p“B ´#$þÞ€ÿ^v†t‹å»õQÙË=æ Ÿwím¸P¯z¹ï%0F,8="å­¬Åà0B#%AÞS¸‡8éý—BÊŽÚ•›:¨žÊðwC|” P´fÄTØ¿SsWxË2Š5(ööQ—iå¢V@vB@Rª` :\vñ·E‰>0qVŽ«fN'g~>ɔŚÄ½Ãi¤+ 4AM÷ßò¹¶ *r&lÃwN´5jdHÀ™ÒâçpL¡Œdáš›È(ð…îŸ ü2u…ñμ»Žf%…ØÁ E«0ŸT#žmÕå¯Nz^¯)¤„}â#$§T+q_”À¢ÉBd5š¨#$'£ÛÌÁã|Ð%5ü®ü@˜Åå$í»yë÷»–n %µÉkÕï¿€$ØS"€ùœ?–IMȯ ã @c®™äšÄ…A†´#%ÛDhækŽàf@bT·U˜”œ•wlÒ¦E’²™F¤Þúä/k<»/ž/_ ’|P8WÈÓ&=,Ñ›»0E“QˆÈ¤ä™!C*S'2‘?0½å¾P<õU¦¾\É<¹JŠO/B9Áã©È®²›>6H;ꄲqêôåáÃ#oá¸ÚO Þ|4w~ž-ÄÆ!甩Ù8ôp/¥ÄFÜ–'|#$ÅÁÊŠdSC–˜Zöny¾À:cNEñS“—¨âŽ¾|0d1×èPÚùf>§‘;ô^;zO(0äîadc®W¸ ¢&#)ŒÈç¸#)MNøàé-r©N3pÍ!lHåbê#ü:;†W€ò3 \ä ¡­³[…¦Œã0¾‡¤ƒ±Åò&º¸Hæï¥-ñ±ö+ŒÈ®¶›û+èbq¸ÈiLµ6y–ûʬKka&I‹ü™ÆIB%¬`+ncXÃt‡ÔîáˆíÅ5·AÏyÛ,aNrœJÝØRø}Ž7NG~Ôqaq/8!Õ„åýQã¦s¿–ôÆ ¢:Ó‡Ï"æ[ÏŒã¿LŒ•ƒ²SÓ}/e{M£pß1èÝCïànPIßãËAØäŸgÛv }؇?O—I$¯/¿¤¿ºýqîë¹F«Ù‚¤îTj¾„˱–#‘6¹ƒØÌýèßc=½ñoU±¾l/ë)¦@ àPC¾G¶oÀχ:r˜Ö¾[xäü%~j2 RôÞ0ª35¬Ži¿Âሊ¹\¡–ôUºÎgP”ŸÏ".-©u¿#)l1£¸AÀIB&Ú¢Œ˜\ql\¢Ø {øÄ#%øpa²šÒµ íc-„ºƒî¨ù÷‡Œç%g¼ çugÒ¹¶ÞåaÎc)€G¢¡g&þ#)q\ƒA6*âÊì°ëü±`½ÓæâµívÜ_ ´ùh²ÄÜRSh‰i2+»–šñOÐþÊW³˜î?Hs\xâðvÑ•ËýPÄöv2&/Ýpe0T»¯Ïs#j¬)C‚[Ø $¡#)Þ:b¤\CÍidë;ÈЪa,Ú8­ïÂÅ*±¥P:¡=uÎÕò0Ó!-®øvAÍc5 Õ¾>{\Gàp­©ˆîø~äÐÁ#)8éžîo,|QÚÀèÛq"x(y§¤(´¦øËåJ®‚ôîׂ@¿È™ÛGï¾ÿkß#2CÛ§Ñ}â±Â´ß,Ç7`¶ÂU³@ÞÔÛkÆ¡Žf­W]³åEAuÖ#²ù<ó×N˜g}vØ!ÓÛÑò”ÖFÅ}Ï xL‹±2mýÙÛÒ²{‡ʤ—¤¿ÑÃÙ$‡¼à~;GnÉñÕùš6‘­§Úº)ô½âï›s9a(0.#)ªF“aó.ÐST_Ž"p±ùˆØZˆÎÞéìÉ2ΣÌƾKdJå!ØѹíT¹ø|1<=²s¦>9O{55ˆÕy¦D¦|õñ=ž ê®G¡i—3ÂfzÄÝB…JªO4bÑ®>m÷eWkZC9š÷BhBöT¹^þýùô9ù°:öãso‹A—f/-@‘‡;·‰á5tw3ËùrXn€²’„Kî×é0I².Š­–Ê,ÐZÅV‰¥áEáÅ5XìöL$·/Þû±[Ô·§íŸ6ýå‡ØûÒ&5P9êÞóŸ¶;xÅë¹P±§ËAgÙ‰äpδm¨Ò5IÃ[{ylè=ôVòcÛ¨¡¾5{#)JÅMZQi"Óc#)m9÷kòï9Êa«:ò7æq<xJÒ€Vx(p¨£ÐR  ½FÛ€H1 º0ýÛ…Y€ã¿p…Š‡›ë«´Ñæ3· [¾æêñ 0×Дþù#)âHŒêZ½ß®o^Ηíóžô„›÷‹þ¦³Ó»åì箑T ?ée¿æJa‰Ü"¥6»._¥;v¯<ò¼ŒQ¤•XÓþ_÷½ÉÞµM3Å?|·ô±™xøŸ¶Q5OÚ?›$ÈB"Wáçð«á ‡…~  ýñ} ó£Ë‰æÆpè>¯g˜0÷Â{ñõéìÈ‹V”ŸÐ‡bŸ¤€¸?ª@ÿIüŸÈù¸ÿ¸Íü¿w×ä?áÆžÉ)nx)§%@‚—À rÖ#)G˜#)|z4ï(M8Rë×–»÷Ü*;Ž%9ÆéóĤ=9Øꂹn4\I­Tœ§A nM«d³ðáérc£Ç7!ßêÖÄø× ˆœ!¯ª\¿…Pc#)MìO›æKò1¥óú(w±4°èchlH¼±Eî!Ûqm‡c~þBD†„Ë#) $8 ƒ>ƒLìÛöQ¥ú[ò?ÇÎÜa½¨Öˆ 8òŸ¤¶Fø`c9îíµ¦Á~òç­ 84—ïêYUß‚;h ˆ X#$Qø#%²;v°bú" 9¢‹ìîühÜ4ªßãR§,<ØŸOä°éÊ¡«ã¨Ý™I:nBÖ#$È¿`ìzmë$†Ç½“Ä}œù´¿¼ØH<JaE¸ÜCòÏéȬüz~øîî©D¾˜-¦Úš îÿt7‹í"xaK†Pô V%Ad f.³±@°äƒ”BÌ=lTL`‡TG¾ÆÅ9QdBÜÀ‹Jk)€_@.`˜#%[)`?lsóÊE¸dš–KREºš\¤t¹AdŒ3(hK©T&Cžë vâÐó¥6 ÂŒ¶ ´\ìdV; FÂF…L0R,\d©ˆŸ#%ÑA â¯ô¡BÛ=‡|~Ÿ?R©þ›s÷]w¡PW†æ@3A€¼°%ÌÁ€re¥Êì÷ÒøE%Ü#$`é¿`|äy¨™¯CSÊQሩkÛWe ÙâQ~äÀgÍ>Ñú/Ÿ½ýÍOs0¡=–˜È̆ËI31áãz¢tí">µòÆ#Ï1Þt®³}î©ìAâÿ|›g:ËNšàÝÃ9#$$Øn”r÷Y<tÔ'izOì2p€‰‚ö#)¢#X¥2Q*¬AbÝòmœ\"&óOe\š¤“#)'nÑQj¦' ^#˜qš²(¤© Q“çU1L-NA÷acàxÕ¦É]RÎ#)8—5j#Âq«1>Äìœ'w˜Ô djI§§íjÈŽÎOhê¨ÕeSþ7‡hø´ÅÙ’¼.ôyí6z­Ë¶Źèì,ô©»›à+#4åž¾xãã;àc¼þ™±M‘ëq¶–À5>Û94/`x›º¼Ä’Ÿž#^–x¹# žEôõ’Q¡E"°R1HŠETi4²Íi34£õ~ó߇â1ÈÑUT ,¢ªvëëÖd8̹ÌîL¦›M¶hîunã‚ç‘k+´f.è2#±¤fpĈĘ›}äÉ 8ðÂì(vG°ìÖ—3¥žß;?«Né“|[XÔ”ï Â#)B)Q6ì7Tmq2çKž€sß#)á¹Wu¨ЛÜ]Bì0†”èe†DòÚ_1r×› õ˜ ˆÍñAÂÐÃÄ>ñ„ &Ø4؛Ø`|ÓÌ}Q(ÆΆ´pØQª%¢¦Þƒ¯Ïmq²}•|jºLÕSvÉÄ7ÂKšã[ÈÓ¯„ ¸8†'‰éxÂCÉWr=‘ ¢–#%‚iXìwXýÙÁ]¼,qø)$#Èzéc# ðœS¹<Ñït²'ö(v2†I#)×Ѽmµò×5²V±­,¶’­ŠµdÖÞ*ºZ¯l:ïÈó»ßuÚ«ìûÝ6JJH´Ô…RØRŒEvÔ9 svlðƒ¨IáÛAIBØzé­ÇP3'K̂ѻɛõÄy8R)j4Š¸~f|¼>_\ži5<û“s?|y@£¥}n‡½hð¹J|&/³Æ¡ÌÓº|¼lY'4‚0U"$"€,ˆÀŠ”&Á´—ÇÀ¼ï’™«xÔÝÕˆ`ftË2/Yݘ%žÂ7¦¬ jsÆðdÈ#œ†Òð"Ú¤¥”™$‘–óÅ:aÑ y»Ššä·úVº™skï±AyDaÅrË+¡ŒI#)ÛF¤4A¼ #% *˜¥D"H„"ȵs·íC"¨‚P‚Á* J‡mîæNcôãDïÒŽ“¯@0v‚¢-!TVšôÄ"1BɵOl–0îfàʸ#fTâo¦z÷_ÂbúñF1"‡‚Œ¡6Hj‚"aN>»wC\ú6×d=}]”®z<Œ…tåÕoÅ‹1¨”Xy#%KI`ɲ‚ G›eùmã˜=‡G Ú#$›iÑßÄ’<’ø”IÛ;yHsT*u×UášÊXq¢È°ƒI#)wó£¥Y¿ÄË"ô5$@›š¢»£–w@Nɉ¿f¾˜ì¬KBC†æ#%ïÃF¤ ó=xË# å£æ¯¬{–Òú7øŽ}Ð8Z’4ªñ'¹‚ˆòB§ÐÆ,h¥!@¸O{ɹÓ5]Ý(–‚èTÅ¢…#$iß5Î@ÒŸ&ó9”b°f„‡¸JÇ%î=Á×ãæÅl~fÐÜ)¦Ä}Üjq¤¡'°d(Í´'ƒä©¢Wʤ”D@É‘0Ë,‡EÞ@¡:{|ÑÕ ÀÐæqÔª¥ÆéËc¿yæ¹’ÜÏ 5XY}V´ö˜)¹xZzwwy `6eÙ „Ù”Þ¡þ·Îl+¯eªñßñicŒ‡;%™¾// aŸIA¡½#%3Ÿ·×ÄÑèæ@0‚¬21¶3Q/uÔ‘ðï|wÔz^îÝöõ±§sá¼4=¼Bá÷D¨ûzÕÓ­Bá“…“·ÍÎó$œ>7¦üNU<¦Ô$À,&øgâ†a¼ygѾŠƒs,³#$q ‚`ÛÏC.ë•q—»K›ÓKUB¸ [£ušÅM4£Cn_‹ÎÌWœ¿Œ¦7|Xê> ‹Ä>'ÛÑß;æ×¹uH†‡[§Ï$:úÞì#)D8ëÛ݉ƉgK•\!S£›ÒÚ 7E÷vã»·4€ªKFˆB‰- ZTK=H@#)AbHéâk#$•5¢Œ¦îi9ešÑÅÌ`ˆ¨Q@œò|iè‰Ò¢¥Ój ‚°pMzvÂ4Þ§žLhÑX·J(É:C«ˆCk…YÎ_"Ë)Ȩ#)M蔨#Ý9î,ó]»íYôÖñ{±zÍÔR´9ì®ã¾o|u—ÉÔÛ²Í;§Xök)l¢Ís™ö¾‡¾ÒÆ»úàÖñÜs¾·²:£!Fê bÎPÖxÜ’âw=E†fü0Ô÷ ØTVU;@éÝ‹ìÐ_n’ˆux#͆vØ)š•PÈ·£Ñ}uò>dÞé­rœ´ªŸ™I}q^2úqä¡Kg«}'Ð% pÜ&ƒ!^#$sVE"1rœN”³q2M’»h¨œ>°ŠE=zê),.Ôœ+u˜enGpaÀÝEÉÎ ši#)öûÿŽÎ¹Ë@Ölª"r£ Q"BŒ-a3‡–}%î¸Æç’CÀv|ª•JÈM„?3'³µ{yé¹b+ê¨roå¨åZ$kìÃüãSÙ¾(l.o¡6Èîûìn<4”ç] ìS.³¶å,ÜsL“M£ä6H#%:EÓû¿LØ™çÓZ©#­i!Ñ:®Î!y¡®ÌÙׇÂò׿Y¨†ü{Ã1)1­„Vyê;KZE F¶/†o¶g#$e‰¸Çlî 6Æ;?§¨ìI•àÜFÞDx?×Âë´(ã¥@Q‚ !Œ]‘7N§’47k'•¡•k¥^÷ÖãœSaÝFq8æj\Ø@I¡TWKH"Æ&‹Ië]TšL˜™§­#%UŠC ©Á£FÛ)j…ÆMqbißSsÚs-HsÁÄ©r#)ªkD¤Sp‹'Òhû¦ŒCÕ½7n’ëóì:go8™g×.°<Qñ© Í‘`¢šŠ|*x,ƒÚ$õ%=õ_œq É»#)ñÁÕ¶@8™¤ð…w@Ýß=¾æ?Å_#$ÀïêØYÜ[^› Þ?pщ*çÁiKûŸ …®L à–?@w?gÙŸ2“B“ü¯Ãyìο,ñaèÈ¡4y#%^‰g¾¾A™¨@#)|¢€ÃáÃ+¿m#$O¥FvÁÇ"`>øV%ãóÞ‡T•¤+Bé]u{*½ä³Ãôþ9†F4O•‰ôï×í©â#$˜â#$›^»/„2Š\xÑn^ ×{ï#)qªÈšDÃï¦á.ÏÉú@qr¢z˜zñOÏIPæa‰íß¿ç˜c€o¨ÝÚ'³¨åá×xÊJ‚nÿ¥øâX¶³OÅF5ÇÔ€˜œú#-)0Ûß5òš $Á!˜}\ž¿¯÷}Жüó á0¶(¢¿¯÷ŽÎA‚fd€-RtŠù?ôÿƒÞüA9ÛAߢ=ϦÿlÔ®¯FUýGµ¯/ò¢0ìzÅŽH¼¿VaÚËt.»2‡yòˆh€€ú—¡Q[^Q»‡àß+·Ž[z¯o Ezñ®¹DE‡ôÿ“üÿwÓýæþoéüÙ?åŒ9ÿb_ýò곬ñŸe#)¹xÚ]÷ÌÕáÍõþÄÿnÚÛ#Û]ö«ÛY}ol*O_—ÅëOÞ‹Š?ó€$f'Þ{ìw¿Zœ€1SLã(}kô>c’[glæè šýÖlaÚÚôÍìç?—ßô¥¡JZ„à’{Áýÿק)ïÉÊ)ÓέoïØmÜ’d0á ›Ÿ§ÃË¿•EôGÇ^8ÓOçaÉ…5·÷Ë–K%Ræ¨ûUÊþKæO#)#%ÆFþ?Â{ŒÆ¿°¹«÷sp_¹ˆî5ÌŸÝQýžyû¿ˆëﵸo›»ev¹ç€´HŸËAÝtlÚ¬¨veclÿ!4eZêE[m¦]2‚sLž<‚ öžq¨.`@V’R‚⣎ñŸšÜ3×,œŸ_ü­Æ¯üN&³Ù°[ŒÛ‘ub¨ÕÒ:V­¢¸Â©ÙÝ;Õº)²6Qu6þ!ÂÚ^„†ç‡fbPq™Ï®ÖÍ”ÆÚD6zíuñ˜#)ºÊqÓcþ¢-2ëó=s³×eŠË<jü-v3°J#);¥_ƺ/ˆ÷ ”åE#Y}ÒϯÙóãôºž”GâU)n1ðåøIÙ÷ÔÛöÜÓ_rãce#5]uŸ‰¯è¬ ÅÛÈ­K¶ä÷<—ŒöýK™ØE]‚ #)ID~Ÿ‹ÂÃ׶ ~Ói³/¤Çý0úãáXcüPÊÔ±ˆ‰,‡ûÇ.<ýú~‹èÕWƒ|¹ÑÇ„œ6ƒ†lXS (û…p¯Ç›”Yòy6UÓú Z1Åm™`7'Έ#$ˆ(e#yÚáÃÉÜ6oÈp€#$‡“ß±ƒ£ðÞKìî¯õþ-´Î^j+a¬yαm?.¹Ÿœc§ù½+wÜ(LÌÁÓÏìZ[»žIÿ㻓ê©H"žPƒª#%£—p€N^=•È­êDB¹Ör…sZ;~¿Ø½Ù=ÿ±$…lp%þŽú=!€ý¬zV×è‹péâCsr¹Ï¤,rãäÓñáIââ{¹JkråhòÓôÓ×(Ž}`Mù†|†YûÀ#%v†ît_÷лPcñ_˜¡#”~¯ýü^Žýuˆ|Nh¯NÝÓòÓ\{Éþ2=+{âÒº°äùƒRr¸cl sNÿyùñGçÏŽWìóøOðwŒG߈ê¥|Qñ„~ÏÌn¥Ø`6ØÁm3ú­ý~m#D$•Ã›?Pïòù“B(xäñÿFã£C.„*ê¡k=wȹÕÑl´/ÙÎÇ”ë‡õé@{;ŠÔþÒñ—»ÇN¬ò±O>ÍÖlè}<Û™¨­w=7ð";Þ¾Š.Ÿ‰×i¤¤GEBø>"œåò`m‡s´éÂLÉ #$Qê´ÁÞ¿“…·¨Gvþ\¯A¥=’`–„ d¢yUzœCŠäæŠÅýDô#ÕXÚüJ†B¤'s…êfqUüœo¾Þ}“ºµd"Èr*”̹Î'.KK3‘G)žEœ&Ój#$@W•ÑȪτÈA”Ùo$\ÑrÁ34Žpê2FñŽÀÇ__‰‡Uò%Aª¤I©#)#)"u±#$¢ºKj"ÊË+:ª°z»}ˆ¸ˆ"ç‘îï¨EÄO=™?DÊÀIË#¯@#%Û…{òAÒ ”óHßC’‰&hÒ\> –ü¨ˆ€}à >¤݆ ÀwŒÜkÃw‡÷Ô@¡ùôa¾æ—°?§\ [­¹—áùV$0ü蛬ñƒØØ+÷c[t¨Ü>1½O.¦Ó "s¶^ ‡¼Ê_•°ÀO•GÇâÆ¡ƒ\jw€ÜŸîï|H Àý=|½Ž¹ü€®Gíí>|ß‘Û…i¿/òwüu+ìíëÜ¡ 'Dyy~eñB7\ýü;-åÐ9ðv—5[ujŠ Ž¼¾bJr Õ;‡'àQ6iB#¶O+¯Úì îg~ŸIHúVí­üsüA‡…L#$¨Í#)~ÇH€YÇí‚zëaÛ$P…ªæ‡¿é[è¢ÙÏb»ÍhcDr‡ ÒlŽŸ·ö+êAPZ  ˆ¨ZÅ¡Öëz৅«$uIŸ7*VÏG­ü¯ úåï.ä^’úGùk#ùs=$¡.¾ëk'¥ü ¡pNÛ̧Tï­?÷¥ùÐæ AO;‹Ûßï­BîG÷g˜Ö¿…mx[¿ÚÓÌÖ^ëÂaPáá­L*w§«ÃÅàϲ®×{x{U´As\C˜tìÌ8ëÓ°ú3W<—?Òøþi ¶æÆõ„DUÕÙüíÅËT"ðô'{·_º#)•IQ'J:¯MlsÅ^醰ù·pˆy34u½v(jPq•T_>gÉÿfˆuæîÈž1÷Dçä\ÄãÖ³.Œèê^ÔNœú£mIþ×ͱ$Ÿý™ƒç­ñ'U碡ñó‚û (¯¼ñ‹ëÍ~5U:èu劾‡€¦w¾¾Xuyyo·˜¨ÑÚ¥6ÞõŸ¾ªÇ8sï·Ø añ»³­¶‘i RW–FW´ÏÐå«Å„Z±ç–j®¢÷ct/Êá•:X‡÷©+U/¤Î-Þ¦MG¯7F‹îD#b]lì_º}qV;ÔöÆhñÎÐt·…¾Mç^{Íඌ˜bµdÛÞQþLÊ÷ë´tÛb¾/Ÿsºj0AÆ ®+M¼¸£”é´_PWMîªÝ:tçñDË?~Ä>1^ÕÖOWJª(ž›S'Þü“šÆf.VÐO‹¢vïsγÓ:ñZSAh?dfœJb.4S¸Ã†›³ùœOÚúÆúý¹bÃ(:s)0RôI¼ò¿#%yêjª«³Ñ½ùæÕ·BÌ;{ûÀyÕ{#ÍæjŒ†ä&7ô çÏ_nÅÅ¢Ñ]ErV€S’¥£I#)^ûÙrÕŸºŠëš[Kó—‚F~Kû<¦Í†É°…û5«ª¾úVžAÀlËóÖd!SLÒk=£èvÔ|Ç ‰t#R~©ÙEé÷ÃMQ˜’.á]"EûÍ'?^GpfóÛh„-ÙÌv÷'b*–Ÿ.:üñô‰ÌÄ<\K.+ŸªO²Ù×é…Š;6oÏ}Íú¹s2‡x7ÏÅÖq Aî!àÐÛÖÓN¸ßzÛ8í3‘ÆéN¹pŠáMO2Oã6]þâœá§î›œ˜¸ª•#)Mê¤ÆôlÐòQ*€ïo”üY°}Ú;´*ÐV ÕÏaãù¶½·¹8mÝúÏ«¯âÌCÎ2ñÍeµŒ+9r úÞ#)—ãþ4Ç‘A¹Š§Ì86O„o•(Y¼“Ð%Ì–ê‹@+ïÉûÌÉÒ°n7Vª.Ï^öIßSG…Ì$‰A°Bn~m¡ZØRC3)GñÆYÂ˺{曡¡–»óMKýÜX°æÈ8ãéé#%¶ÎËHe[¼‰SËýH…%"슕ô¨G λìUµü£Øf¹ëõÙp²{}³1µä¼ÎƒKZy3^åÎêÖ"âXò2ü¾Gôűôó[x'ýq#)Gër>6®q†õŒEfXÄJÚ=;³ŸàÚ‚ûká-¿Òü£aÖ%϶_Ö߶+¼äâ«ÆB¯„rMù[]¹4íÔû\uëão»½ÐoÚEþ½Á¯Î×%TdZ@Vàõi¦LŒ˜eƒËõ Ü ø½`€8>¼Xï`·b ÑÕ61›YŸÐ÷H ºššÓÞ7õz°Ç»“òtmÓNubÌ6þ="ê3'•’–¶üJ¿?qü'ÛŽ¦S¡Z–0«+îùÔògëu}âe6R_™â`ó6Å™¢àˆïÀ„Ð{Ã/’cõÑà@–L}=6ùã #)H6œÔŽ8{ù·„Žµõ{~pÁ}¹ü.“› ö²¨“˵ì£AÙ„¤¤Ñ÷¡gÒ¸3ša¤ÙÁ0WÁ¥åO£ãŽ%×ÐL´óòüŠéÕÛNÎ?Re(éò‚tâaâ#%·Zô˜åî<;#$«H=É ü·«M´ Rƒw+ ÑaJö0£ˆPڸ朒zƒÊÀïí†#%xybXÌ|~ožá¶ŒÉ÷U„éå¿åPÔ˜ø è6Ogyþ/œ×J‰ÑOñ ÷Axr[ùg5¨ù(龺'C~·˜é øLŸÆ¬ó_^#)óÖÓ²#%©ÙŠÆŸQ,iLКvq×8™gçBzÓËJ†Œ^ç‡ÂŠß‡×&SÖÃGÍ«ÆB6ˆWÓˆ½É÷ØÈl°n~·õ )ÜÉ.òSÁü›;÷JmuÜ©VÅ@ÎF*¨› ™ÕUþ`Î×âIŒ´ Ÿ#)ãaÝJN îaÕ—£‹9£Ê¡È¡NŒ)(}Òv8a4º'¢‚ƒ#$ÔVÒ¤Ýg—i[ü7ôcæ <¤}»-ˆáæEºÆ­ß–!©ö{eÕ´zã´± fA (ÒŸt’Iz”«$¬T#$)‡Š¼aŒZ¢Â—€ãr?t*(¹Õa0ú¥'I#$ 2 @‹e5r’È3ùÜÒª!Pòy%7Ó³¥ô:‘­YÑö®E‡½ÝðV+Ó›{Mó¥ø—Lw³-‹™†qÑvyD©†rõ­ë#%;‡z?4צ˜ÅC­Í3µC逎9c›÷}cÏßu:Ãú’ízÐör/º„!vr¾éˆ»¶`è!†wcõ6}Þ¯ÞBLM|¸ 'ϹÜhAûôŒd;cù.ÐXËüÜS‡†E‘aÀ–âK§î ºX‚Î>4<~Fê”W_È§æ ‡²šÂö´#)ˆ~â'ΪÇqOèÂè8g  ò[˜w zõmý¡Ñp% p!£~‹; *ŠÖ@ßE“Ô3ás©.’#$Æ¥gþí_þ¡9€ÂšÈj„>U÷g}T°¿­ï}ŸçÕ m¬è`ÇÉùÝ1P<+Ž(o¡!ˆýFtNb)Gúhp²ßà>ú#$¾ÿPdÞó[9@jÕÌ Rˆþ fWAâ¾?ëDÏÇøÉ£øEº#$OG •©§¼_}•|nؽ#%ÇSÒפHýîòǘjîW>¿ÙöscÉè)$¿”äå`·æÌ<‘FûNwŠ]Cá<eŸ¦©P®æ#)‘ RQ-ðAÑîBÇì-ˆDßAKQázÓn©cÛ¶žŠã÷GÏ‘$ò*Á‘ƒõüGíCéçõË~žÞ.A˜÷ö¿âX¦ÿ™|q€™ŸŒßòéIaÔö?ÂN˳àüÅÄ° ’s l·Øõg}KÒ§`ìûój³Hòb |„HcÄól@À|eF$[EÅÞ÷=¦|¸VÉÃ̸“ðr"H‚#%ŸE²óSžòáxäƒÛf‹—•FŽ+mu¬>`´&ú—"ƒ˜~ÌGÄõýÚ®ªêwfÄ8§ÁÜÁ5(jðTÛÔ3”×,þNN!J`BUF#Ö,¯ L’œÄHR¨)¦¼0¢ªCI»¾¿·ñÝõpÃdÓ»aí9úÌa0BD $O4lÚ)PPíîúÞ)¨E0ð¿,èý©ìåÔšÃÒ?f`¿„Á<ø8鎃»ˆÜÇÓ\óžx_Ë «áó;y]6DÝHvSdH;åî’H‘ EÆ^ƒ¶/2K #ŒDNZ²/X¼®tæQ& ýòä#$=0œMïžòkðÑ@¥6ÔÇ¥ã¥û­ÍÁ#%¿¨kK^ýÞ“V_ó`wÊ~ج»…h#$³u):ˆÜ3o¼M„–`Mtv=ÑÚ”ËLæÕ½#%g”©ØõdäÖÜy¯ž“VÄØRŒmʪç17F~ŸPibÎo{T;ðÜ7LÛ,®&»©1Øfe7ÖqÕb·Þ`wbç×äÐS:+ǵ8í\RÇ`i>Û¤94©¨ûA=}ø0­½üo;u˜èà ƒ¸§Aùm”P•'@î}\ò¢¦Qˆ€^-ñƒB@Lî½PfĤÀ³e!#%œ† ˜¦ ¹‘)øbñISBò¥YøÒúG¥›…{G>±¬.´ Áè4aëã£~í¥aµï†¿)Q@Ñ(P:©”©mA ešWå‰åmc5 Kù—Ψ¡#¾#)½½_‡›ý,ôe3Z ×Dæ‡WÅ ³Ìs½réµ…„=o?Ù¯hmWÓ÷ã{¤üt|Wƒàøf!Á¼Xp=\k©Â‚š¹–³õ}«Yˆí5ŽK_þnÿXóhÐSÚµÿ(xúà§þoñËÅN„’WŽµWídJl/^•ÀòŒ 9ðÞ~%V¹ÔýØÉô¶,¿ÍÕg€AeLD®RDd{$!c“•SÌ\8t¤#%¯žàø”#).$¯­Ã½ÒÆ8@Va[…©E¬û1KšnùŽyôÛ§îG9ÈoÉrŽªÍÝ$W®ªU'I2gt©/ݶ‘³kâœâ Ùz6,•4éœ!Ufœ¡Š$ë{žû<_ã•)gtoâ´C-žnÁç¿)d¸`(ä.¢V¸ñ(#‹¦1Õ³ÛD0+^7½'k…lò`ŒdÀðV¨jÇŠ&døý—Ëe’ÜñÚŽón¸\#)¯Þ|ï¼hàn#%+yÓR³:ÄÄ÷N'zŠP £sU‰–=QglZEfØ pýê/©þ¡È0 ø#)â‚Ìÿ0æ¢z"Ôæ2Áe¥™ìêVº" dÐhŒ‰Q’i½vhg*ëa®‘±²¶89¨¥¤óˆ„¯O¯4fÓ•†2茿clòÓßÙ‚2¸Úáû;'¯"ÿ‘+¼“Ô.’mëXÎïaÜÓÍiè¥iÙ²¹¿/t¡ïßÕIÒó„«ãïíÒô»z§öâ±Hf`ÌÂN¶»±sJ®I¦Ê¥6 u,§4)°©¶ÀHf¦ØX^+¸Å–Y-Š¨àd!#)…w&ó0M)dhÀ Ê÷» Ch7#)•Hj8£PCÊ^,°kÅHŽ2ºÎC%Ï7èÝatNr™oë0þJ"RåìNÓˆq†™H)`%\‡ðküù8<½<Ë{Ç#òÑnÓâ|Àò0VÆ5‚“‰ Åiéè{ p Yûý#%*G²¿ˆÚ8T°MˆÀbq¥¹›ÖCúw¥‹öÙgâ³#À}™õ«ôX~]W§Ë-åöü~xËŠEÚ¥ š>]µ1Á¥'f´z¾u£º6´í„ÚH…?/¾wƒ¤}Úœ¤…‡aq´Áäãdí*$öÎ;|QÜõ‚îœv?¼„ÅX{§§nª!ù/ÙÚ~]ÙÊÖ’HçÅ0J×ÇDi—.pϼ «gLlCgø+¡¶KÐòÅÕ²gµR·¢¥kYKÑ‚¸!(M…i1 züÓ¶¯Þ#Ànª»Ð0¹P#Ë`³šAdºåXóꑱä\²EŽ™‹ =¿'›KìœýAÊhµ¥ŽlG¨‡æxX(8zœÒPâ†#~.½3iÒ<#/EŠÁ)U‚SmÂ8 Íåµ(ôÓÔžO.‹tI)au0*Û¡º!4ºOÓ ¤)€á‡g§aóV¦ƒ¥‚“ëž__ DÓ'8z:õ1=Ôr§ % $ž×jD¶›U äÏ£Ê-2§"lU ´_ÞbLê¸óûYOÁê=¬@…·^·ùìü4-<£‚ŒU¦”&;Ïo…^âëBP&œÈl°¡DMú"vŠ¯aTçì±j@Ö94E/}0·]«D8çnÈÝ 5²¸ U«—µÝ§á©€}]@W:ÐBœý,áçÁé §D;aA¤ˆù®­õé»f~¶¡ÔŽç[ûÜô‚ ™èëXP§x÷º|æÐtî#)Š@zJž$ü.{%™ï¨3¹+n^M­jP/ËKY…¡­Y ´sÎœ‰—]U`r€Vë"˜ðh¶œ‰pøQÙ—pœ4+¨M¹¤åÇNFó $)Œ_Âêl#NQÓ<õÅ% h_™úŒ1øÛ5ã›ZÂìouX×Ò}¨«ˆ =Îéƒ#%T®Fæ‹Iâ)|Ùò½q_–Cfƒ³DÖ9>Uoº![úÖ’4¼bBâP Ò¡´ÌGi¢%00G¨B°À8Š§Ž€v .¢MkäÞÛ£B<ãá|óâø °m<Ïh¤]¥æ7rÙtST"ÛP(aZxdîÍ‹L|µi±î#%spW|S:nˆ=£ˆ.¯:ZúÑ´ólZì&Û‚ t„‡-O‚ ¥qß0<â#$‡è9#)*ï‹@h<b”º¦ïîˆkô ^D±x!Œªìtæé~%óX¬ íçÒciNN^dÑ<1š/iÜÒŠŠpTP¸À3ßšcä}º1GÖI»¹ê‰ní ˆRC™m¾'‘±¹Ÿ hïîTyôæ—2«ÕÃ8³ 0ÂÜÀ¶=NËšô(춶Á£Ï µ\²:ä±Û]LéÖÈo©uŠázãÐÎW4ˆu¨†…ºÎQU÷#)ú¨˜£g@îª#)S‹ÐRÐÆðƒ6\×¼S{éJª\u#)LSÇ›ªrJh¯ŠÛ':ˆH«FÔ|­QXCJÔ¤#$ö½@Ë5CdÄîÕCòucë-7‰=+1¬üέ¹£6zòÐѶ*œÍÜ$ƒ—_^FGJ1œN³Oø’¸¨a6àDº¥Ìî>\Cμ³e«ÎCÔ‹fÈŽŽ‡Ô©iëªQbŠy0ó½ªQ¹ Gć«%ÓL¤°°ñ, ÊÚŒ(œ'8Éúö4q[Û5ÊE—!ASÍ«)Þˆ¼4'Îw‰QðfÊ9~¼x:Ø`ÐQ“³·ˆðÞ-¨‚@®4á#$¶B«n®<åàäpPœ…X˜á¦›¹1Ù}Õ`+CzÔ@¤€RÅt 5:}tÆuQ(zÜ÷Ø·¹ ³ßë8ÙÞ1¶é#%Ï?™‹Ê^PÖâi`Ùd]ÇÛ¤Â×êœuéƒ æŒÇ#$,ÃOq¥Ô-ÂËn7uNŽ¨fàƒÖ#)#%V(ª™1šy5¬žuæb†´AØQ/ãuI:ÉÕMwÓZn@lêx‘ʤí¥ÀR馵Eº´AÛm„ôg†I˜ "Ë8ÅÙÊ#) 8Xí#%OJÃtíïEçÝêó–rÄ÷'…(ZÖñÖ/õìƒÚ© òé–í@„Q»w nyéT[@i|¬ P#%  7yí…ðº«Eq  Ð„y,éžÙÛª¡U£Žxm†RtÞÃ(eŸ7ò¹ U¾¥ÐtÔC~žŽf\õ-u“êtCf&àÌ)«ù}ùôýÿÆ=~Qëú2‘ʃ‹|Î_œ:”j>Œ•Dÿ’Ë'ÛÓÈ|M)©TîÈ"#)V±îpúcé%•¥P*17B£\xÉÄÔ÷è:Y©Æ½0GëɾuŠé¾£°n#$“ø{©­;œç§0Øx0ð‡ÂŽ«Ç@Z#$ÉÙñ¬f¼ù¥E7«ñüד²¾Žl6þÛGáÜ<š„¯¿õ½bÒ•˜^µ›³_c=Ü)DüF(~&Ûm9®Ð4†G'ä/wCŸ~^ÿ˜U`ßCÿ!a@›Õ¶èŒï :Ê¥@"qÃv?ñ3×_wùÀ÷áýÜw}áû}ߪl$þ”?œ(2–#$°@ Ï®pj‹+ëÒ—ü’Š¤§ûgø‡, !Û¢¾hó0‘Ô]áA±(²· À©`ñ¿Ïh1¸ìÐ[?ÔÏå-㮯oeŽÍ ·;s,6ÿTßdÑlí¨~!‹ˆ ä8<º‘…öóí7#)š(&Hl:Û8œ¹‰^®fÔúüo6ñ`§†%#)¦½þä,ΉCébý Ô}ÎðB©Žà#$¼Ÿöò[§Xx5ÌG¬{AØ`ÀÞô{¨9žïoû_C“ó/Öæ€9šCÔ.È®Q_{ƒ¾Üu šYiØ^åÏßwûBù&¦@6ε ±ô` >£RÖBt›c’`dže=kÐ8 þìTûýK¡ÝþnÞìÞœò~„ÂÞ êŸñÕ¢U­úeöãÛtŒŽPKM=¡Î¯@2q#$˜H¯)å86ú ’HFÓ<•^¯d­ÒUÂ#zX@ª#$ ÓÎ(•¤skíþøý~~Êì#?m‹nÝd´ Õ$o—OŠçûï~¥nÃÚÅÌ ü$IŠUW\#)–.û–\>h“Sé4n)jJað+|Á·€¾Ä2†ú°9ÿ¤–­†ž!à†êü¡Ã«÷w=“†Æª-¥ˆ`4¸ÎŒ7qÁ&ÊFü@"äl57+#%aSíE8‰ãá±4æ£]ˆžØ €DþG¨¤4§ÏÊJ;HÉøÄ$/ÓåMW+6ç.I¿«ã¨€zƒ›ÔG@qÑÖÛ°bÐ}¤™ìMv#jüèn;îäk{J‘’ˆøÔ°—Œ PP‚‘.˘#%ïap]¥(ÀBÉÓé-ÀŠsw1¡z#%r$s©V6Gal‰‹9'C :ÊÓ#)àóÖÅü¨h4óŸUx^®RO¯é ÜŸãòðþï/Ê>‚¡,ÿ9PÀ<fÄá—º…D¢†…ŒÂ1¡ÿÈF¯þæóþÀ:觘ÊKïã–™ý_æãùSþÆÕ´‘û„³ÚžÌ%'èÁヰ;ÁcÿRã]÷«Y×#abå{ü·¥˜ö”dRHõpi2¦Žtµ¯ùëËË\u[ï»nѹ·ó£Œ†‹癲ÀÙá0"²HÛ>?rs<i)X<tõò¨¤ªõ·a­êŠQ´$ÓéZ©QvcÝÖ=à —ëÁÊm28{‹wínÃa\æ–'ä1Ô‚(5„N‹_Ÿ· ºÍÐ~©œØïü3>ÅýÝ̳îÔ5½÷òý‡Aˆë=eL¤£>Ü–Ô#)Xš·’Ò “à4žß'C’?ÃøT‡#$ü(R˜þ#ÆåA%S•.¼®zÍ­îu„:”Î&`t”àÿ¢g€hç‘fÌþw‘ÌXôƬ—kçÔÀeî#%J4k«Ïøu¢#%dt’ FpgŒbÝ’K!V.íÝ$’H™^«¼ùû6®(óÙ†8°àصß`ÿ)ë0Þôo5í#%^aÚàêFA„#%Ù=8üLa´è6™.pw;²Í—uK6ÁÄ¢Ì#‡š/à{öú½à]ê4 WûGö"øœ OÝýÛNÙò:žtÉ[L_Á¨4\¿q€cjÃð]þ ñ¶ƒhÉg©Y¿‰÷½^“çºãkÆv²#)„Î Ÿ›TÛ|ƒÞ\IctÒé‰bŧý^ÝûÃ=<g°ò°u:—¤2„´DÕ’¡ásëŒφ¼/º:iŸK´ˆb©?¢Qô-¾Œ¡ÂØKH1±¡ŒM™$Î.r¾x–°RÁ¾(4#$D"ð#%i`øðÛ¸?z$ òï¤Ár"æ\taðVƒ/§éÿgB>pú:{ˆ„J¤„¦Ez:”ÚnO¯CÔD4;#$ñô9·'zÝã#$¬”@D|.aŠ"©£Ç{X¢º4ϵ,ø#%ŒSŒé}öTZR¦·Å‡Ó8«È½²d¶K#%¹²¦šMaQI–$Ž:Ñ’˜1èddh Mž1§'·¸¯´54Œx°>â{f ³É¨…#5ÖçôðY;ƒ'CXpëâ, {^2nfóf!Ž¦OHZéN%ÔÁˆCS9"8P\r!õ¿áéüçÓÔ?ºå»oG×Ú…ÃùþþYÉAkZS?Fl¹®î?oÎ5àÛú~[ßz¯^™)"1‘E@zo‹ª«¬·ý,UÃŽCfù¯ß»Jm”ßb•µš5­oC"z Cˆ*ëœDKº;6ñ™2¸Zi#%8pvd’BI›«mb“3ZÍX\ɨV]YkÞõ­ÊÌ ŒÆMÃN°£#)=<[y¬J2þÜ3a7Ã{Þ:²Æ´ŒÚÛm¯òE Â9L²ãìøƒPYPI$9Àª!¢ ƒÊ€#$ÿ™ïêIƒû¶\9;ø7 çœGär¿âþŸezº-­ÿ3‘žZ"‡Øí#)¶©\ËT’}5{5tˆÄó˼ÉI~Šý붼Bó»ØŸR>ß­4ðàΔ h}„:;í lF,]„$$·|ÔºåvÕõU¼>ó\È?Í”Ät"š†ÒJwÔ["t*»ÕÙ¬$ˆ €r¶QI´|àÉ’tÿ»n5Œäй5ßwu¶ÛmtT¡Û1À‘·¤¦³·DÊùÌËÄ¢}dñrTÒ±ÏJMsá/MÌ¿Ÿº19l<ñIýWêÑ{LjçÓ.O–"lCÈ2PZÅ›Ž—ÚP¸vr 0yò‡8È*Á@A Åî_³]:µ)¹£Sšå¦ÈF©ß?V±Ãá#$ø×ÉöŸ`|ìu7}ÿ»+hôvùzúŽÀ0¿GéTžä~–1‰yrŒlV$õ–»Š÷¸? ïÚ·¸Së"_Úv|3çgŸÈª*T”¥"ˆ§ ›ðÏpSF[¨5º¶È7ÁVÍÝ›è@1ÆRðK:P"b~‡ÈߟK]Á¡wh?Ì‚ßÄ}Q8›þsˆñœ¢½ÄÄ´ f"y–ô–‡òÅ-î†ô.¤$ #p“iáàÉ``È3q°šnãåuoˆèCO €¸YùlႧ¢“+@#$„ ñ·ÝôÏuçò_î+¢[ç³nÑüGÑ¾Û !)†rŠJ =+ÞÊO|\B,À6ô½`)gÏB£Dûv‚y:«Æ/Ú‡žŸëÅ°ùŒ¾R{ÑÜs£§Ô4!‡G¿0ÙrEŒX¬Àæú\#%ú½ÆÓëƒáº‹Óˆ½_¥ùΣäÙTåcÕ?=NÍÉ÷›Ä8@w+ÕNÂ*Q8#À#%Ïb‚L&KFÁûCÂÁ€ ypéê®>óÄ8»š0Fǘó™òÚ´ £ÀŽùÝÄ©kOõöj{{{!` ¤"‘HÈCÚuƒ—ÞYéSúŽkuaܪÃMž:\èC¹9>v†÷‡ 2 RÇ̽<éI2yCoOqUôPú:¹—ûqõ–¾$ÿ-´1Å„ýçé+ô“å>K·­éÑÑDÑ”=áù1»ÝŽ‡_ÓùüO/WÒÜ@Ä.¦¨zTÉ@³òÂ2I$õ€\U<‡Ð·÷fV+›)@H‰çñüâþ “è:‘N¨àl hiÙÐ{(¿ºÇ/¦QÕçh€œÚ¿nfµkr­¬D÷b07½ƒ2eÎ2é/†F>#$0…xý>èì10GúUIX t|C<wÏW~a‘gv}J¯äcì<+¿„¢]âæèà¼àS›€±8†¥ƒ™jºoÀíûŽí‚Ç<Äã!’ÎÄ`"O[tž‡³V¢"R>RQÀɹ ³öÁ.&Õ#ƒ:ë“„K‰825–6ÆwåòqÂ÷rý£ø;_W»ò„™´¶ R–6e8nÝ3Í0”`Ì-u‹r6ÍèE±½„îzLy†çКtpp"˜UÜ?¤ïè™aƒ"H,ŠÈaBH„–½<øs;#)¢JD¢‡éätaéa*´ µ‰bªH’´ÕÛÉ_(>î?YèDð < ÃæÉïµp;7ðœÃR#)ø ö›NÍŒ;ÿ¶o É^jªQ{žÏ™âþÇ×_ŸgKS ·…àÕÜotcz’zä·­?– ñÂÔªhrOòÊÿf«(Wÿ=Å(ÃkyiLÕF9(©t½àÁ ™¦.š‹kEª¥(šG#$×ÃæêëîžsºNÙæµ°>óßGb˜)ɧç¤ÒØ=§ ¹Y*Ö÷vÕU³Xã#)¹Gö&^·rï4|†Ïd¡SØ#ZBu—ßøŽ2p)æ+T(¢ Pˆµ>"w§é«\‚(m¸gZ¢ »ÁŠ#%Å|‰À.ªÏTì€Ðä¸K‚ýO51ô/™Þ+à6R4xènã7ôò€Xö—,,X!ê(->Â/ò1Ÿ\­Z-¡@ò3”{¼yòþ"¡ü_Í7îŸ:=Œñ³ ,Ϲ‘¿ªoBaMK•àfšÞa—Š1+&¡"$®‡j p@CPî“öÄ„Ilƒ#$Ê«s2”¥õfÞèbǵîJë=ëhµ~”® )G”Õ(DrT#̇㊢9¦ CF¥¯ÐvàyìËÚ‡™þQüþ9dRdƒÀ‘’H¬6ªÒÆÓ}¨lÕ<áøD§nóE25#)>TjY((9‘'X´ƒ!ÏûŸÑÀ:<û¤RÝ/µyXµê:!…¡çöÒ%ò´éª«) à}w‰Tj–EdEE‘dJfQ¦úû;¹­zÞ:­ÈÍ*Ce²·uç™u©åÕKD´‚ÈÈÄoNAåôz>œ¯ð‚oQ%лëòž`õ¾Þµ#)#)#%ëãpÏÚÌ EùÉ#$–½'¸€X2W“ÀjlâÚaÖm~Í«!ú¡CSj"˜B Š#$PL,àÜáRŽ#)CžÓ4,D çG,-d'ƒx‡š¨ºîäìÞp“ÍŽþx×^UÐ/Á~ Z[!Ú4“fäêÜŸž}TRN5 7vI‡:ŽšUÚpÊ:ˆóûwnìˆVñèh9óGc–sÇÓ;mÓoªÛ †Ü‘.0#%¦!Ài» g1‡šÚ{.#Ò'—š\%tÀ•Lú#)à–€DøþW•\.[ºjŠEQE¿…`©þ0?‡Ö|Ò¸ª©¾ÎX÷q}šì!ZÍ|Á½ów6±ç©PÿÌôclþ´MŸ`´*ÒÓ0ÃL˜a© ‘Ð9†õ(õ,’¬’HúÜûX?N#%áëè¶ð4 4µ€jbA Ì©hxÆÙf"ŸI¿‰ðãE!ò`Ciš:1HèQ±ÅÅ åÓüœdDèÙ±H*àš`0Š|ÀPø;=>ÏÄ~þGQy0œ³~Ôy‡z¡ Ã¯§oLJýÔQÜBØ0CóÌN¾_.å}|ÿ®eÆ”ñl+¤~îVÈæñŠÈÆ()(îOS `Á•Àd‘a¯®èÙ¡¸ÃgsÈBø—Â}gÝÿ]} Ú±þD³Ñá•#)ÞÌêû~¼vsýâTþ¯©òÔΰôÊk‘#%üêi’„e ù”ª´U£²¶ˆ‰¨@ÒBæÆgù5=#>úÐìPOg–K³V0\###T}Œ(æ[îP´Æ¸“‡¨H“‰.b©B €¸K‡LQÕò#);»_Ìíȉ@çЫH¥,A ‹€Áƒò¸.ØïÌÝ.<Îjäž cù~°±ÙpÖ}0¸rÝÚlÞò#$¦õ;ípp"#%€:ýU’èÁÁA2ÜŽ—QÇ9†/§nƒÒ§0æ!½íÆN;¤²‹pýAÀSP jèð+½77XvÜ1S§#$0óÄ:JåEXw™Œ`ÀQ΂½}h£W6xtú¸B“ß±T(¡œOEØ Ã#$#)ÔXÈOçÚâï×üþÜjd­èÞöÄú—m™ífÚÊ韵òÿ¥šâ“GU/±ˆü˜Iâ:J@€Ø¯Çéµé$U$M()‡jC&Ó×JÀ£‰Î{Q#)Ø›—óÎÇõ³œN«0_žÚ¨Î`U‚*dýi(®Sòª--lm,L»XWW#%òû;„sª*ã°wö§ÀËS7Þ3JxqÑÿ³°ÚbõûE]§*æ¿&ιB§ W‹Ts<Àû{Df“Z]OBd+î(; ½xóâìDé‚È° ²8%vÛx„ôÓý>¬jI&v!Þ¥jpî#%æåÏók{p´É6^@ä¨gCóSQ3;W‘½¸Ò0!2#%^6‚)a€ÂJG‘¾ôWß èCÁä¡×ÙĜгÐüBæLŠB«Bª†ÖKš9éãÔëÔ{§ŸÈÝEÚS(G™ùBlÀ&¸ƒÁQõº!FéÔž9Û»‚¦PÞvŒÈ|h¹Bù‚ÿa*2<~Äkd‹¹{`€¾¼LMÎZ„›b€~Dû~‰Ùçówö–œñ?¹¡nš¾Ó£o¯ûÄ‚"ʪ#%•BÐR8¼‹ª <{#h`´OËísã؇_,øúÙîWà×é#)Ÿ…J‡¿é­˜ÓFÖ Îh؆ûóe}ߢÀû©9F#)fƒ™ÌîðfÏ?ó~›4ìë fñ`9Ènúªgêí¶ÿmYkíÙ·ivýĈð§>⦨¤ˆ€MÐ!ivB ¨€€H7@h#%‡3#%z$GäÊœ}ˆà…˜oZ ð$i–££ ˜ÂÀXôPŠR|ñ÷w{ƒ·›0ÃëCh£®##—‚,`²#ûúDàêê‹ÚG’óyv¦=Å@Yz„ÄKAP ÓÌtœ½äÐT6üß“cqôæ6O@èSÈq¯,pž2‰Æ-Žƒ \«8 E1X?éÌÄŸx=FÂÞVb‚ȶ.{D‰¹¢v‘§°9.ÚHk) Ø0–š…~>ÿŒú#­ÿ=çÛÙ€V˜@b·Î]×o:⇊ñwkhߟ#)i™õÝè1’d%ˆ>§ÄØCxÞÛçK4ñn0×#ý¦te8îg#†µ‹Ár”Cű¡xÉèñ»èÜcë`•íÍ<£oW×VHŒØI,×Vΰ `«Œ#)5ó~-sñõË–wiz§ž^DX’Ê sl«ÙãèD<¥uÚÃ齧?oê´"{Ã5ô ÅÈ ª¿:X"˜{“ºzϡωÄ@ÃãÍWß×í¸¼ˆbŽßŠSpý¾z;½|JE”B ¥wý˜êN#$y3ƒòðI‰"—·ò߉ý§]βs*íñDG­[j"‘‰cÇk®3~ݼÝÞZ¿ßVòšÜ_êOì౎@á‰r`*³¼ŽÕǶ¦ç & Ö#%?~³óœD÷+mQ±Ezõy„ëºWÇv™K}o`"‡âI¹˜|‹óÿˆŸÛŽr¼ùøT%4¢ÔóþŒ(ù–ãó/Ý7OtöÑ*£åG<š¶×i&ç)È&䳑ô9{žÃ§ÏÆò~ÉBy XK}2<<?œ10÷cþˆÔn†˜öö@ž™Fú¹ðj`sûz5\Ÿm4b~ˆHÃì{Anw˜ôo/v½ó¢¯ûu:=E5€Hg‘PÊ:qü<mìfÏåÛàn„š*›oaØúáì#%:è/“NûŒÄ=°,ifüËáHSA@ÒThž…•'/aP±@U„XAØYÚ°ë>aAÙü-³ú¡Ôll#$Ò%ˆ»½ý\KäHâG¯ð? }Ùv}æ—k„3®Ìí`ÓšÖF]€”‹h„ !7=g¬w=Èn B XQá¶û¢»’²v?}îK¯^”Æñåò€ä9æþ}*?¯O9rõæ®[âÿ+•©ãørCNÀÿyCÆ3ú¡#)àÑÜÐALDz™¼ç]3-“ÊgyånØFÞVX2h¬'à2dA`¤À!Q2¶Âjjú‰˜æ‡q]*j…‚t ðÔÂÒTÌRȲ§šØ&ÔþóäÍ Ô„(ÿçŸÐ ’¦¶c¯aj1ÿH~Í¡f(-4ùê"bâMr“ANìW{¢Úi«Vn#%èÙŠ¾á‡hF {j#%÷D™q+Ô´çhUˆàÞ Ä€ße(eÇÌĺ^TÀ¦~®“Oñ½ßù|µ®lY( {2¾"£Rñ¯àŸ† )þ“¤ðU+%E÷¸EüòñPÄÈæI:ÿ´i3 ÈýŸ£•tWþ|ÃÍë¡ù};3˳ž¹Ó§^X0¸þEA¡Ih²ZKH³N-N½l‡ûû”ušùßïþ¥ŽpX>ïOÿ™¿ØyömD~ÛÌâ"¾TY.îËšï1òÇ{ë×Ú:yT{,eÃMÙ"bŠˆD!N_ÍÈ"oñó!N¹ùsÚü„y£Q‘AX¤,Ć9ý.)B‘Ôè삾×%£¥“Å@¥Rð#$ œ0¼*5#$@åì'ŸfÁH“5BmA$´‚Ÿ¯ƒœ`‘UãÖðž$Zä=Î#)$»ù@‚ÃÌ¡”-Õ»R¨$#)hî‚&ý¬ºsR?³œÁàg#$Jª¤6 @ÊÄxèF#)GŽú¡W&IçýŸÈìÁñëäw$!¡Í·þc¨¤\²L¹pzï´´Ÿ$àlCÀ¾ñÁÃG³ïƒ©·´êâ=ÆÑÃSQ—ä_™1›¬äohü,£ öÏñã<5:ý°Jüµ&/£’ÂKDÖÖ?bì&Æ8g:µ‚¼<(NrÃ{)t¾Á5úÉï..S­b<‡Q´¼>\‚€áwAÈ¥r õ²`vN¶?ÝÍþ”ÿ5ñ5Üç·l<ç’[Ídm¢†k$éÔþ{ ¢á•bC¬ ê…­nÕ-eòt×½àŠ(›Þˆ‚p ÐFÙ¯Çúõú1_lþåéz…è/ø^§ý?ïO`«ËBòþ7f d#$àç²'Ä+T@~_Ë¿20A/ÓZ¨PínÀ‚ª9Cm?}ñó¾o·ÁåÀ9Ñáøªô>ëµQØH>ÀÀ"Ü]4¸Î¯H±µm䜅ð{³´ QShr¨Ueæ#%v1m£Q£ g à>»„:F+¨ÌÖ'ÉÆöÆÛ'Ðïaņ~Vtš.(Fý±#%÷\£ü±Æïcî3¨j|jÏ%Ï£å#$pÜ((¦„A¥äøÆãÚ\îtùϧ£ÚêÔz›¸éx‰àŽ1¨¾j—ÁâZî£Þßß!7Fí¶° NÕ;öuŸ‚ø{7‹ —CT.­”9XðÐu¾úèÐ-Wûä‡9@’ßIÌ®¨Ê/„t_ÙÿµÚ(›åd35²––ñMêopvGW=töžŸŽ~ÓÁ9GõÕù>³´Ì?ô^áÇ«·²Ê}í wóvñËÞMÞsXVÂ5&ýöü2Äk·Þõ28éœLðÑÑ{ïYùu95·¬lÞS¶ ­L#%%ÅE…ÁnhDSÞÑÖpB“¢¹;@:Óf#$x)|8Ú=±q}žDk˜ë8~ò£½ô_t›'Åëi¤X´Ü©l Ïa‘9A¹vT¨H¾»éä^ò&¯Æ±ã®ûÝ&ðà9;¹ˆmÓBåÜÙÛe‰êƒÄë± ?8 jÛ2òÛYµ©oŠ½“ýWäî×Þ;¦š’¥š*fˆ*ÀA#œhÌñN:rÖh½k–q‰F‰g§KØúòýÓÇb ë‹ìyþˆ‡ò~¿ËØ]¤</^Rµ|ëÓþ#%LF>'û=Þ±ï?W×/Ž½¨è21Š´ôä¯a#%nÏ¥T Ì3«„º]¥„ZÏÕ€g馸хF5Ef)‹Ñ#)*p¦ù·\ ÇÜŽ9â1³k;\¾ú¢tŠÃº'óg#)J·ÿ^úË箇ǩôæóéwqµlê4-u«Ò…qõÜõ§oöïÝÉç<øŒçËÖÞ3RãyïÛÀÅ×rcÁ‚£~ÈÑO{ùñÓatÇúÕÊ7ôÌ i ¤?MÒ^·N©füM=;k¦š†ì§ZO‰QJ##)1£ƒ]Ç@ŸüïP>Tú?jrôB¾ª‘ßÝäfˆ‹½\l´Õ瑱æ"ã÷jð›iã!±y<„C™QýɼÒôßYŒø|”wÆGýÍÇ3@iîÆ39dx4—çðO™˜1£¦’†Akéù?jƒÏçÒc~¬yAi!˜tÇ€å ©D3Š›'““õïßnËØÃz"ö71·\MÍKt²=þxGÌ}ÙË8õ5øâó0˜ÒД 7ré½³2cœ§.%麗 @ç#$ÊvÁþDІ5×´>4ÇQèòÿû“NM´@yÔJ w=Ú`:  Ž%xÛ&Ù¿•¯X”WËôÇ;òktÏ:yĶ5d#%ÄMRôÜó³–ZçšT,ª&€«I$ý9¯¥k ÔIP›w"‰ÄpšH›•H™Ûs#%•ÍÉ-¼¤9ÊÃâä˜z#%z"+e¨Á.·•éK{fý Pgõ±(›,þ°ýïòëb¦¡êX¢Ì;O)##òþ?/ôG½ñŸgèðé¿ó×lø»Bš…y¿ÙíKpŸè›[<Bgyü±¦’*¡Ïû¿Áïñ:ï±°öˆÒØaôJkõýoÆi³"ŠdÊžªOͳöw\þŸÜ>Gõÿ7øŸ0ÜCûËΖƒ†Ì#)òªª°ÿQç`]L ÷ÿhÂÆÅÛ@oÕØ›bq+vòuâs4#%¼Ïñ·pô»Ù? (»s{Ä¢4ÒR$`FÚòOòh\nÛK†Ñè:w=o$¢OT ôlu:Ü¡„Á°Áf³Õb6I.CÑy‡³1G¶I`ÈtÑÜók$à]K¦;kyTUvI·¨º[ŒíÎppå{žƒžñäçÑ~· è½piF2H`ôDAFF ây…Ðà¶gö›çë6ôˆª±ƒÒ©ZªUqÔ†Àu(2íäg&@N0Ï{ôòdi†7l% Äõ~´ýUuþÄ)ÀÁ,Ø”a#$Ã-î;gáåb õ”stëE½ò‡M æZNþePž£òîú†¸aζÖÒ_Ž¸]}ƒ>x¥P‰ªœY#¬éuÉÀa™ˆ0"PPS€á)¤– ÄÐŒF"ª4aF Ì4Xpl¾à2ŽŠg†ì ãí Ü Ì+¬Bö¢Ò›Kì»×À¹±{w»ˆ Ëh3ËŒ†OFU R÷Ô²’T p³ ÌÌG2mwU@ëP¨O—_{÷iËî$)Ñ—OªŠ—»âæµÒÖŸœ#6lñÆït~wuy©c•]zÁÎve@À‘àDÐÉHMЪ¨Z4d©‘bŒ#)³±RjE`Ò2!´Á ó¢†\f§¬#)½‡Ò@)ª#%HBÍU$+ù_ãÿ3âß•™ ´Q~þùºk+",#)ÕQAøQ,¤‡ô;ê$øHv‡¹TA*îÛ³éwÁ‘½ý«ùØöE{s¯sX‹˜ãûz— ø@sôéÐÕV+åÀÖ?ཥŸ ì˜T’àvIà§"ý£:Z6v9jHÆÚDÁ‡+!#ö¯v+mXí⧙uþeßâÊc^Á™)6™®Õ|i%A±ºIÈÄ8#%ªÉðöÖq‡wØZ^¬Ú1Aµ_q=ñ’“Ð%0a„Cñ)ö˜¦I Ö1ˆ1íñãÝmÞÁáí܆Ûør««é ÌÆq)€Øã›Û»mØ.~ãm‚­¢wqaEyJÚ.¬ð3d*Äì,(Ô_Äz¯Bíu4ì⣤ŸÆ¿ÖÈ“$¹‚G~˜C!L,²o!r爐u‹@3sÅ.sw&9)(3®ï#)$]G.ÎYàå4UîHc)Œ$®I©“Íë(è47„¹¹S>¬§mZÕ_+_ƳÏzëw—Èû£Ë$qã9iNŽŸ=tõ×g<Žm¼µf¦I6ÏKÞ¸|ÝAÊÜV[:ÞfqpeãAÈ.´ƒé;ÀädâÚñÒu;öèNí(âˆl{÷màó#%'.R:.ýO˜pi°ÖA¹þŒŽTÏ‚ør†É‘Z†† ž;˯[‘±…„ÎF'Aw!CßüµM;}^Í ºv‡Ì¢uƒÒb–2~8#$#PîTߧ‡^Ý|ý{òá̪(ÏapA* ¢=è––ßÏ^š6¸µ.ú0ÃÎbmè¥U6ä®^.i'¢é.=36ýàÙ õÙH0˜u¸Î&sZv,A"–»œ ° ” ŠYzøÇP‰#%œàEY#$ÅòÚlé2i}"p;•Ã‘Íö:œa¶œ[%›Cß„jŒ+°Ü—7ÙG"&É8#$ j¿aäóó‰;ÐÔØó ”Dó8ÍØL«¿[mâñŒb¤;r°QˆI4Òd°”É"Ï6ó#%±çNž!ÎM'¦3z÷?›0Ì{½½Î¹#¿Ü°y±Cšöa“â p(Tv:n.¤xðÆ {ÎäQdé… J#)•Ì5àÌ­6 »&€qÓõÕEv(ÜS¥ëpSi6›±ãžöêÆíÉ íà¥ÆðŠnIjìxnJlŽ§!R„;SØUÒPÌÍÎ åP=_a±‰ŽÂÄj#% g~¤­®q$wtoTÔ”ØÑåŽ}·ÆôcÛ*ö7B”B$$Z€›¢¸å‡‹Í`ã1É…UmÊe¥ÖéÈÁA‘r‡¿xÄ„ëL#$Ø©‰:#$B@`ÁVXy£¬G"PRA`k4ƶK³.ç„ä±±¦ª5"·QÆòƒccÝõÞo¼û-ʹ܉•Œ™ ÀÅQ¤â­T IÌ£ÌÏNØrÑ,\à@&Ø+ǯ53¢!ÚÌÄ©™—›™™™clÌŠ¬Ì™’J±Û3Ñ/#¢þ­´Ïvt‚8°ÍßV\务 ŒPRAPÀaì‘´ãyÙÁ­-?‡¦ý“VW›<1¸æ½DÆO(£-Ü[žß„-±Ê~yÕQLÓ®²Ñ0DqR±Š.yRÀ¢=.½4õ\ô––[®‹¢s[¡¡iXïÑ<fÁ¬M“32Éð±D‚À˜B™Â8[# ;Ž+=ZÍÇ<0ƒŽ†F™:õBP sÜ®Ýù4¨§ÈBî1bÈ”µI.©lªª¨(ƒgM&þÃßÆôC:íT%{V¤jQaF ÅÄ,Y™ÒKb_NiÖ˧Çn$|n–›|σ=j©°*–4˧p†S54Û‡q˜ŽSE((ƒlcü¶»#) ÿÒ#)!ÎtœuEEZ8t+ÌN[›*„#áÁòÝs.Ïóß´Ô#%@#%#)“½Ø8…Oq~^‘È´Ói!Ç¥¤GÆf±xF )1õtû—¤¶| ׫véǼ'ö¼¶Äç¯Ñ@NäÇ@®„Ø”^¹nj;ù \«•ù·ÂkBŠeƒšÚÂ1Ñ*lˆBd>õª5fɺc}´âo#%o! ¦ä”Ûˆ”ÙàæÖ‰¬@ÚMƒGnÖ9¶<²åÈe‹›‚dս‰î6) ¹ÁF”Q™ÆIÖ Ñ¥£ÅNKÍ£SÁr„”ç¶ì0m#%^ïfPÂg?äý;íì8ˆTB2Ql;DÉ‚!’ä•…öÒt:*“"è‡<3˜)#$™!鹓‰,¶B 2Ì„‚ c M>¶ çi(<¹•#çM˜C—-M5*¤Òp!×CŒ‹©Ö¦u#%lf‰ÛÞJcX_è~¦GÉ4Ï…ÕÍÂ]7ÙfÖLƒBÒ=À1ݱ‡ð ë™6Ÿ(šw‡–#$¤T&#%õJ8^mÜL÷3tLSPbã3Õ©Ø5Æ"LÜÏ D#űNÊ”$Wêm£H)&»`5Æ­!œgdÜ5Øß,+³ˆŸC•i7¦(äÁš3M¦4妵«Ÿr-óãØꌨÁ¡³ ÒòúOsÛ#8y…pAŽ»Jwx¿UÐÐàvì#$ÆHñD‹Te%’ÆÑ4Ùªf$ß3‘‘.ñ)1Ð8É ›œŒ<A‰í:+‘M}™-oÎF¼³@ÞG¨¤·´·=(ƒ%h4Íä o ÙÆkK€äåÑ#%å#)¸*.iÒg°©(,—}9¡Õß,¤|$›coF@/_\l".íÉ_ŸÅ&Cx5tkB.»Ê\÷ûvhWWÖ:5Ç/*ÐÚ’ñ+ùø=õ’q>^2åÌÌF†˜C¿ ÀÆ=C¹¯uÐH 7U’MŒÒÛ¶âT#ÎX ($ý=(Ý‘ö˜1önS^@Èw'Š)õàW«Ð£)«0i)ÅW¹6³m´ÌâÂiŸô4dXjœ Þÿ<0òN~¸£Ÿƒé»¶Ét¨Ž2ò.„èGØEuÿYHBû<cóÑ>–63­ßüèÿqŒdGu‡f3Öø®+Ôõ>Þvn/’ÿÄúcx[­×Ìj¼®¦äÕ«#%ýÙìAé‚…¤¡íól #%/=7wz5…Ëƈ¶#%ár#)XŒ,Ù«`¾´$ÞüÆÃää â#õ@ð QR ¨4Ç”ðàžþâùbŸŸÕ‹1™þ¸}ÿò3ýóòÚ¬Íÿ6{¦¦¹Vï7ÈæESÒT„$mûÀÚª~Å=›]¦h³6H2’#)Ê:úüÝÃÚ<éI#%Ÿ‘Aÿe›‘}G0sæcæ @!#¤LK^wQ&©¦l¥ ˆ¼¹ÐâÂÛ€Ò0€“¡^´SDŸ›ô÷Åï6Ú-ŠÏº—#iQU/×e·Õeööqå !Õ#$¹ØÉ K<âá•è(ï°‡ê>Õ?¢H—`*PÔ#$QÝ”¨“ÈwµÒmŽVYÜOIê²°ÿy$ÓkUDàt‡‚x!LéªLC̯ÙÊH„"F1lK E´WLP÷æyˆH!$'Ý>éršåŒT°"f®ªE@7 ÖÌ@;‘CÓHm¨@„"âk$ Ò¾ý@À*&S,úæÔ£Y!"ZИ¼ó¼ÚdFQ¼µô*¡X‰ ìÅ’i+æj‘ CH3 µ²(Ý#%zûNñ•‰ûì}a‰¬U56o“«‘´’'#ªÖÑ~vÅ]~%Å´R$@èA€ Èé eSí§Ó 2cSÜB¡‚ˆun%"š#$hRÆ9iet2w…[NUÜUÎÜzE rãÇ ì?ˆEóQ2 C¾#$°FçºçE¼DU$@$ Å ‘GÒb;Ho]·äUUýªÚ¬mXªª5¬ÒAh‹‰F#8q~€›’Iè·çQSXDáý¯4Cí‘#%1×m(þÀVVÚåâ’Øj°ÖÒ0h2#$Â#$Æ–G¨b%Jj7øÓ^ȼW¥”’E½îÞ=kr½›{.\Ì5ÝÚI/.î\®kðï<ÛÅÍÒ9ÑÝ\ÜÒVWuŽI>¹ÖKÎC¯kÅÒÐÔ¥Oøsà@˜5" p¯÷NB1`«$BH–,!ïz—°ÀÀH¨{&(\3þYC`#)7t¬:ˆB@’3ˆxN·hø^OO¥þ,>úÛ*ŠŠçŽ¥!Õ#)%F±7ð:îj£Ç=­1c-¤‡Äß7 àü ®™[8V:q.ÂWã€ãššEã$¯Ú,#)ˆb$°|^"„l°Ùþÿ‘kàŸ.MßÝOÑS #$H©œD‹'l%b( ÁŒP"#)GoÃCD™ìâ=>ÛòÝ€ è•ý¥S#¹«5•ç•|å½MLž¾ÿrI¨âðd²q`)N)*çP$ÙÉ®°.Ô¤™"¡$r(im ÷R¦÷3šXR\l“Ëò±UÀ‰ z l©¿‹u.ðþ¬=T~ã –ý!Cµ³û,‡ÕýóžÝ)è§è8Bk\†S\ý_3ku'°û<b~!a·q© ¶­V×}í‚]{‚ýdëßO.× !ó.®jübŠ=îã³.=ß!mô|OpÞÓ‘W'­·~ÙßÃlx'—Så>[o“¬(qôŒþ9˜‘ÛlÌýºBŠ›bsYQ}'Ý~^ŸšˆBUJ©E'_o=ÉÌïYGB ªyé3¥ñn(ó‰  ÐÌQöIøƒ 3!d/E¾qN!;#%ZaØÊ.©VOBOuåÔQŒ…†Ÿª'Ï%Dm‰°eüéÝV³K°ªâ”0‚¦ÿ#%øߟÃÚvý‘²Ê©Q„U2»àMÁáÖ”#%E>ø\.¶1‘`#¶#%m4µŒ’¥[‹M¦ªÌi¶MJتMVThS2­K[Mµ±ç8$œ'Æq·Í*NPá±ÄMñRE€##"$ˆ/G´dFȾ|) €Eœ=àÉ´è[øjâÇk†@(3mQú"pÈU€ðÐËG8“21¡`Êôƒx‚Ø#)²Ê…û¢¨`¬X²1Lãxi ù=¡}²èI)lHÍ䆲‡µ,†ÑÍÞña¹¡¤£"Ð4†´¬À_%a™1&@`˜ªê±Tv*ºh @¯DÐÝtÊ lšUQ mi"ÛE­8±Ä Q5/åŒFO‡rvÀ¾œ ‚UR›tÙ¶˜Ê€My#$X›=úq‚X¤ m¾þ\ÕJfÛ\Û›%r×e3bÉR`¨¨¢´#P© "²/³Þ÷ʾ2DJ€ãXAd‘ÁQÐÚp|zFÖÙä}>¼-šhÛ-!‰ƒ…Â&Ïr+ÑÑ¡a<ž‰"kÄî­NÛ±I?,¤‹ÞèA 9ÙO×Ýþï³çýÿ‡ŸüÿêÙ² ‡žžr½OˆˆÉ ;¼Ëðe⌠L¦*¤ 09FˆŠweB0cñ4B!E0*!…_5S¡ç6á{î;|ùXÅ\ÜšD´#$Ù?ÑdògÑ•SýPùؾÐÙþûTc ~Hx=˜ „¶Þ.jî]Xcî©¿ê¤èçÚWo8É?w03‚ˆ+PT2ãë6|¨% CÕËýùªùŽôêmÜBœÃÛt%(j§eódùµ#%Ou‚ *}–À©9,˜¼ç†yñ tœ&»Èƒ!#%F‰vÆ ·›É9žþ“£ì£¬ˆìÜP"9Î#)À"üÜnãÀj#)×búí‘þ«5&fÌ#nç7§Ò{7¿t'xC±¡Ý›s{é¡é¶{ñ,Ÿ~gSÀ¬4ù8|^+s×÷åÈUQx< ƒò–¤aôwȇ´ùÓ§„ÉXæÞ]û¸¥Ç?'ï§Ǥ†Ç–™¾Ýþd¹Wçþ98ù#$ö IÇËÄ#%`x ¤änÓž!Ä2!f ï:¡`Te0ÚS?$¡q}ç6¹½aÏ·&·±9¶û<i#%¡æV˜:gqNJ¤ôAÃF+úfîÌgTÀÉ#)r2"08O$ ñ‘0±Å¾&6€g·u‹7êCÄ€È+#$ˆ°¤¨¨zGÄ<hï#$N×Ú‚ç’êSÍ#$#%™^0X€ˆÝ¿gÙ°h ‚¹>Ñ÷l|1$QJªŠ…]—è•uGÝÐñ¿˜¤F æTJ¡`,‚‹;ýÿ¾PÌå jTV 'ŽH«¨<:üº8ò˜SŽTØí•°¿ZCÏA"éŒ`}“WÙ `ƒ‡œˆöï¦Úâ@Ø#%ÅÎlÜÌÃ>ph¡E‹Íd( >@áôY2tz<ý•=µú!Ðu=þ•<w‹)½Vcðçø/½Éâèˆr(Ý—ÂÇZº«%*¦@èWÌTyïÂÚ“¥fÐArÚ¯í†í¤Þµ¹ÌC™DÏŸ3Ñxßù¼”aõî/o„Føçcmañ¿‡ôf6+¥þ#%5Òáߢ`âõ¬G­¸éÚ».ž8ëµÏ+‡ßy/¶y1в÷}šFÝm’¡6%Ê;pûgƹë]øÞzmiQ×éí1geÙFMØÊÏKê.½ùãÛRØnyÇ2@ôAțĶ/¦ç[:òm¨4jáÈ/1ñØÕÜlØÀ·é‹cUnù÷ÁI€X+«vPé±f<{– 4)Ø‹HÊ€¤õ#).ƒx¦÷36Pž|Ò›¥Ôx6/•ˆ`Qèhø|19°ëÓyõÜb‚ƒŽ§œ³hÓ[ûŸs{¼Il0ršÖþN8»uÐ2æ:H!Ü£sk!Sâ/´LÙÍóˆ™J ©óX,ø¬ËSRªƒ¸ÐJ»2ë~°‰z'8uÿ-xÛ¨U0ňô?9ºõXlK£<-Æ;ˆHBºï‘³† ¶ÏÕd?¢tDò¾C‘­ß¸†Ã1]Ä|<$kfœ¶lÑrú8íèí¤ê/@_”"ÍÞ\C6Ãr<ö¡ªâtj|=‘~&@8ɱ»uèÝl¥í}͹± AÈRfBèš„ÝÒÉØÖeÙÆÈpÙ½<v7Â9ï©i>½ÆË7åˆ7s¯e[÷9èy*²ƒ&ç%:ò"ÕT ±ˆÄaììñ9mˆ&1,Í¿;¿Dc«T£’Ê¥SÙXžïo×SÐõ"€´zøodÉ‘º˜`•èêdšž9ö½k„71лȘã$–Û|Æ„Ì0²Ž;v¢r]<½õô/[hßøËe0‡±ò÷oÙÚU>œNʾgžúü#)×8ÏÙd ÓÙ¢¤d¡Ê‡)ZRH}Mê©©ôxò‰ÞmÔ±hm:qáÓNJ½JéËàÒï`8®ÅËŽ¹²«ÂÈ#%‡@ý¾›T 2)§Ë†>ž­ËÏ*‰ñ¨uzñk]ʉÈÞ꘽Œè”Á/#j )HS°¥6KWM»'uu,Ñ4[SJ×oÍç-ÜÝw]kÅÊá ®XŠÆÔ,U§A#% ‚QDEÔáÖÝĈตš—ݘát¸¨¥ÂE`‰dsn°ÃjH$‘,„a$"$`Hˆ’&×úgb;Ãóò.ER#™à±dÍ(7M‚´æ·‚ª$Úú¶Ð»A· _—®”‹0#%¤¿BºmDb¾(ª®ËT°ë°RÙ”¡Œ¡î“€!î›çä0;¨ù]ö·L‚éà‡ß$R@AÛœ›c!Fjd °¤“vÖ Ó%šy¤ÑíJ·vÃɸF4ð‚òaZîBqÑjÛ Ão¢Št|kK•#³m÷­{Pivê&fA²¿Vp4À§TKL^À*¯¡çÇ£>‚w0N(?Ÿ't‡é^útU„»¥`%Ê#)  ¢!È”D¤)P9Œ²#aŠ²R[ H%…!cænÒW:¤ÌS¼‡aõ <&_#$5×µø¯Ç2±¢ÜÕcUÊÑ«õ%¼E 5H‡kLB‘1üh¿ŽâzëÀo$„Ç!×:ùë KÚ›>K?[uUìîzpª—äÉ{p >áÞu‹#$«—/¨·‰øâ‡'…¹2ÒÓ-5‚ç–ýÇÄAñChi¡%óqè)çÂòÑJý¾â–7"<ÃÒÈþ $—b±áäA²@'ù^¤ÔÓ3e#>ðÍj‘ăs&æ…[ä‰L~çýû ÃÖa–L¹ôûü»N}/kñ‚2Üa±Tqº3¦À*ë#$Y…TÔŽP¡=öË‘Õ ‰%bÄPH¡Õ(DY9žS¤uW­_+hwtÜ£PÎú½ùH@bzRÖš”’l†ú…SXžbZ‹ü#% !·[*®æ‚eU¸ïÐœì÷' ÏJÛ‰Àá[ºj‚zÔ¥ÏhÎËOi)œ>̾TLNÀ Ýå nJÒ5'º8 _;ÊšËmËÂm£Ó»Lœ™ÓñÇl2Ãu(8“Ÿƒƒ)$’¨>›xLÄÖõx2#$Æ»;"Öá „Ú]ÃeŸB ÐØ]B FÐ/×Ä@l#¤ØT&Áì#%@p Í(Îós¹,Ô#%FAè@P€×D$%Î^’à ƒÁ K dN/ó¹½ÙJý™ñ¡Ë3Á#%Â%ÂL0Êåy“+H½B¥E@¨ŠÔ]zJÉMAàÁm#)ÜBÖ#8[]-yK­kß|«Çe{2—&(@¸³ÔÒÖˆá="^^8 c$)i/°xÚ\5€ Î)VK+u#I¨†Sa«}ë«­µäšŒ,êR“¦Ë¥AåúùÎx¾Ùá+ßPü0?’$oÑ6Ùƒ&âpl5„ˆèÇ£¾±.¶ŸX‘×#)‰²ôµ3~I½™R0¢„x w'#%\øqO£öžA 2j”ŠJg#%OWe7ÆÄò9S=ÅÜK× lXÛÃL͇½Å6#׈é²D–*·•6r=øà– äcog‡‡«µH¸/'™¿XXyõ‹ex_ÓÂF£N]M‰Rw,~û›×ŽÀÖˆBå æQI$ªhvÏGqýI’Ed ÃE:UN•+aîòú5wÞ  +òrÀ©aÌpN¸9†:„¦%,{"¢é 2ûÎ=âó ’ Ï –)8˜|*^#)c˜>½G;þŒO#%$¦±…Â1$1#%Œ60P?k jF6Ñ’#1à !>á…ÖQœlV³D‚¸`B-Š^š!‹uK®þû=L„!tX•*Øv`%âÁ¦(ê‘Þð9ºó:ù7‹ÌbU ªö«xæVç²@; ‰hLnUjÚ­ ô0`‰ånÐÿ)Çi‰q“HÝT x<´-šºÛ+ÚØ :Rp„žÕX?„ª©°¢d×U’ ^:ÝV­6©f@ç4Â>.dLÄ o›©Ãi7Hí_8àml6ÖQÇÃÇ&ÎùÃ"TcñÉ´f1W"J‚òŘFñ@P²¥tQ÷Ô-ïHn¢6ÿUè‹ý)!åŠè…»mw©åf9 ±†š–:ëYÈ#%ŽÈΊ9V#%6)¤þ͘Œz5 Qb¤"ƒ¥·¾Ï,テ >·C¶{Û^OZÊWׯE f FUíD HÉÚ„`±ªÒý¬Œ’ xݨÁ‰¨ÛYQ²Ŧ‹^üwÆwa}3îœc £)äÀ¬­R ±è§Lli‚Äxp5#k³–$½Z%}J#%Œ_ôoMvçgL·P–+…8—õC=÷n aaÈ™P@zæÄ¥µfs圶ر¶Úä€ÍúSÊ•®ûL¼q¼NQ×;o“ƒ«»#„„»IG¹(¸Ã_ïÚÿC^aœB#%yr†Þ•Y΃N%â–‹´‚Øi‰ I ”Àd"THˆAƒ®áoJÅŽ%‹å— YG!#)(Ò#`7ÀN¶OÁDƒci¥ÈÀ6¥*î>Ì4s8CEb2CMl†¯1„)#$ÈÂj ¹b9L݇Š6RC97¶#)Ç0^Y°Ò‘*%QPM0–†D)„±T2Þõ$(b‘a“ƒVê9vˆÌ¯îƒä›Ëj†‡Hçø™ƒ2J9Ë´$šôûÆ(É­°Ò¯mA3…PNaåcZèL‰¸ÇSŠ)ô‹Ììÿz'˜RBpy?3éO!o4¼;Ha†$9à-9†™ÞkCr™Œ£!W®7óåAÉe†0Xá„—¦ ¡Â#$÷”†¦eœN1½éC Ÿ¦‘8@¶¦®eÅÐ3ažxì¤ß? B:ÿM31þþ¼ÙöĺVõ3Ê"¥¼C @ãT¦fœ;{\æ ]7(áhC;Í´,w%î”'‚JnÙk=æZŒ¾?<7°¸jgËÉ÷>v6Ñð?2sZ'—Ø÷7çÛT­Ì­«tjL;ÀeBÌ]3€úÔÛg\Ë9¦Bú‡ÔbXŽ½7@øCàÞ½¡ uüx8ǘ¯|¼#%H#%Œ@0€ö›>Ž’v£çØgm5,u´PÈe1ÅDÐEƒžK@Lgá凜À EÍÜÁ¹J#%’ës14ÙÛ$‘ÌM4hCJ€#%#)š]u#A`}í;3@Se€&#$JyÌjعPº©˜¥AD7† L S#%ºrn8ÐE ¼Ð+<KŽHÒLíôÖBÎgÁ”c9ÇŽ1eûáä‹$ˆ$^2z3ûiµågš86Mö\žÍT¦òXËÍÅÑÌ¿Úy*#ƒð¸L´VÅNd÷ѤÃ)³¤ÉfeÅe`Îkn§öûb“##$‘PÓ²ÂpÑ<Ôö(Ÿ4Y$! RÑ[~…ÏÜI`m¢ªkÅ·6Õ±­µF­\µº(È‚’Ñ…#%°ù´{<»LæH@Î}C(oDð_QÎô#$6¥” TOÆ,¯G 2³ÄÈóžóŠB#)Q# F„JdÊYµ3L’™4Å&Ø´™P~Û©35i ´Ò¥ ”Ø”)5¦#_onPÉ­&,J%3k4)“)™#2#)4"¨¦%2Ãìî¢#%…#)D‘I2Y@jŒÉ‚Ѳ£E‚)„Q˜ÆLÓÛBeA“0Ë¥Fb#%((HÔÅ4R7ÏË“ÂìíØî¤GÈ™‚m@™¹øÍ­}æê'‡‡Ýž&d?°¦Íì:¥¡*¡)• ñ8yLUdXfbZTt匆§KhÃ~ÛdA0$™E´ô•Ã¡$Æ°wó5½è]æÑD$ü(<ŸÂƒ&íÌÝ…ÂÖî7+âä`³–Šm^»›3´$«”þ×¢8®(N}™˜Ò“÷“­‹Ÿ³çä×Ì/¶åáÃÐ6Øc†µŒt‰%†Àßâ*½ú-¿~E‰Cj4ÙY+J1I´Ë0 A‡3{Ê‹xU/wDS¶SO¹§úÌ<„χŠ¬°øóÂ1IL’¾Xå´ÕIÈ ¶Ë¿fþŠáµ¾Rö:…“Ññ)‡°‡U#,'k=?J||îåüÇ=N‚Á’áT‡:¢Ã+˜_#´1h3•è÷Ù¹uMé—VxSrÔ–0=vâ|[ê^f´BvXmZ¤j„‹D€©#"@0íc­áÓåP NRi§Øà|Všc®.³2¹â×8‡&¡ÓBîïÇDÄb;Æå·ÛÍçl;ôw‡b¥–w³éòÿJ+ÃMœbMhjè(I-·}½zÁ"2¶Æ”°¨"X(EÉQ`»ísn0f'¤f¸½¾š{é·§"—^eÈÖß“}¦½,3O²aÏã}èç6äô½fE‚éA.ˆ(_Žº~šïÂ÷¾nŸ¼'ÞŸ:—Û4TÆ4‡å§0ÜQ^`ÛÉ.„“n&Öƒ]J2š¹~T#)¥=O–ù®H{ß'IB·§!0Èò¿ ŒÛ Cò¯²YÐß2qª¢í §€ÆR{-PZ eµÞçeÝ#%•}Jvìm¼ –Û‘kC߶’ &Ôy‡Jf8#$Ÿ£Ó7Ø@ý=é<Ûy÷7gwB½ÊH C–ï:Ê·Ï£ ŽEã¨Xlì=<B pß.Õ¹Î÷ Xœ¤‘Ú‚»" mña¢ {l¤CÅSÉŠ€:ÀŠÇ¥D±…Á9mÆO6]Nx¦`•§‰R{z¼-è•DªYz#)Œ)¬ÃÒšø3xï#%m³®Í%|v;@È]À’#)Žn¸Œa,å¢ã‚“™ŽÓ»ºxù¾¡˜èajâ;|tíEtëù ÀÕ„ì+©· ò¯óCÔÝ9QúÉ}Õ_uÝžÖÛªƒG×Bi‘'ÖÑGjG ‘ìÌfjVprÕ¡ë£#¯$ŒoS3Z?ŽZ·ªçÛÃ6˸ÞÃ1»Î®™«›†XmœŽ×‚É 0Î&šeXYxqS4µš¥jPÃ1³ a™“¬“0¦Ö¦iš#%=›»Æ¬*Ð9Ç虚%rëÈÛÂÝå0'œ3kUó‚þ;®ü#$"ºÊ‡É ™ŒÕ‹­ñòò%‹"î¦[CÕ“2Ö˜ëµciª[=Ú•ñ»)ÞJÎoIÿaÓÑõ:¦ÊÝ8ƒ½8âáÇqÜ~^¥àÎÑ…ÆÆŒcW´m‚Fg*3#$gõ¸Za™C¶ècP>˜]ªaë}‰eÌTm…“‘0p \±•%¼ÁbQ•¡ó¡‰ÝÙÛO‘¢#%³Á½ ƒ¯0ùm4#%fágDÕ[ê6S’5n°Ò#)qçÆyÒkÕ™wB¬ufB¿+P™ 2h˜µ28Í›{#'[ë‹2úuiã&>8ÎØß@ðc—»lÉAÆgZã5OKK=§ç1Hf2ÒäŠû¹7¸!2é{틇½Î÷*FÁÚYÞ#ÙÕF–®²PâúSc]>Çu)ÔáQÆ€ÂMd}ýcçŸü&ÕÍë}Aßj!àv79ÓŠó‰ÄI•ÎvÁ—yZºÎª3£8¼bº¾jwÕl)$™íÙ¡C¡Í¶ÂªÜßlÞøŠ=5œg-÷ Ûbä‘Åž·’tõx’Ö4äNѨÇ7ÒY,mxHˆYëÄÇVÃeÂ7Š¨ª8þU¶áåêí½baÝävß$éöàÓlg0iŠ3÷q¶ÎíˆÑC툮)á뺔îôg3OÖ)3¦0’jc­Š† )E.äÍ [2„ÖI7;-s•’ZØnuÇXJ4“Òá¡êRºZˆàÓ ª0¡pÃ"õWª^|¨I"ÜßP`QèiªŽZ‡4ÝÌ“X8¥ûåÒê3…‡)¤d+z|Anš¸Šˆ«ÁX9}¢öLZl9”$ –1f5W<s’e¸ÒA\¼E:Ë)‡ŠfxÅ< ^To'a™w´’qIf›lëÑY¶YÊ`¬½F:ÈU ‘ÂãyÞëè¦K•¹µ•±³ÁÆ ¢WèG(å0ë‘Sðk4ä±²”מ¦ÕG8l¸ ØD# ,òCìí %Èíƒ~€âFšw̧]`€ÕÍļ&©·«WseS².k£ÀÍ‹ÉϽåQxâVhlñ]çg6u#%"Æví3Í®L²j6)e§4HlúipXlhyz壜½g)¶.[Mf0ÕÝL#o#%ÞÁä^`êk‹Lò=Bàst±h“4”uÓró„Ãr¦Dm¢.ýkr4Ç-›zÑ‚Pé”tÕ"0;½øÚÌý¹ö*.`WNŒk'ñ÷SÅèqΘŠšt’ EÉ0:UgáoE2¸v¼oO{†Jkqã1¶åÎ2ÃUŒm·ÂsVߧs2Í]7Û‚0•š°¸N$çSlê‘ìfo,Kݸúg:Õc+Ø5¼ˆŒ§’Ó$C×mW³[s&\Øõªcp}´vGC÷™5%oZo4crÙY©hëdöê¼|3Ž¸ÌáFW•¹póÌ©ˆ·ª;Áƶ}h¥„ƒÍYZŠNûTäø‹Ã¢¯Íêéßzc(±i¦,KnéKjh–ÅX#%bìpÍY©€Ë-M¸Ü·Ô´+£ÑIÃ$8ã57V#o‡§ŽRL©²±³8º×cX÷§#-š»Ê马ycÉUc2ÄòL ÝZ™­×[Ñ.¥*b†µJZh=²²Ãv#)sår®_¾@aß ÂΚ¦õ4l®\zϺb5æM‡Mb~!³:¼DJl†ü©vórdž2Q4úA&nÞåÔÎבÝA½•ñÙò3‹u" Ï5/kb¦ÈE9¯#$Åš™©ÊÀÒðÛU¤º9£Q–Nu1hº·¾©-Xlì¦<Æ<¯¼zcg·95§Æ4N" d,ãú’qœ(ÝV¶—jºã³X¡]ëBá$šŽÁnó€Zè…IÉÑó]5¢ú™9 ÖPdI¾èBPR½/KjãuÒ<–ž'Qn Ã»‹G¾ (˜€ðÅɤc†‹å‚‚ïÁ²‘Q±‹Ìá ÌP5ŽÁmÉ·ßEct„6 oàÍÄ'#%ēɇn2Ø[!|¨¤P,È£ŒU™ƒÆ,x€a3Û–šÕ'oJ#%P4y°§ƒÁãH`¹6 ¶H°ÜaF…¶JMª–#)éYf (Yb-f­›0§“I†S[½Õû7Il†WG))"Š—l£ÑPŠ¡Á¹DiÛH”1i¢Ý™­#%åp›)™K¢CÜæø ñ½rdNÏÈàØÔÊä~°au—…Ä.¸¼D‹LiM+ÙÂ&pL*€º2ƒ8ØΣ=s.S6¸²Æ4›N„É©±‡»:ÁçpçžtŒ™} qП”<>ë¹ÒN%q¦ç[´²ã,fuŠÐÆ_g¾y­æÇ9iX–Œbˆd>†jHx!´d}&›NôøY0+4 r÷ëGS[ë}¹YÑ,‚îŠÃ#%5™F°gx9ºšhÌ6u ™Æ3ÀÔã°F¬=Ù0ÄÖÜb(Ó{eb¡ÀŽUEVøEUÉNS!tŠ-œ2±€,|¦Lˆ,›Ažžaµ»·›å¾ŠÆø\Ñ\µâÛÙµsE0ɦ]`RàRS8à)æjI¹ÀäJ†‡#)†€-£‘É8s½>ýq eʱÖ4µ’ÿËPŒP"m4˜ù#-æaqvî­·ˆôHľñ¹ÑéäÚGTF†P‡CT6p–Ä@(Ìá‡#%á‚ hqßyL…àj1Y‰¥ëØÞ@5GÕ‰zX»BVò©HˆurŽ˜dDŒrºIÄé5 N<ʇ@èk©±L/’0âSEK—í5*$Œ`"`Öm5:Qž¼zˆ²‚T–”è)•Ì åÛ•4@›!¦[€fDØZo.˜&ôı#%Ra¡ƒž¨pkU(FÚÐ.H î5êÞ¢äÚ#)»¬8F¦Ð2.:Ža³0s˜:0°h”ŽbebXß{KÔw±˜mšÚ¸72#)fb]ÖÄg8P©,Ôð#$!À¨6weM¬™Œ™«5UýäµÕ«ùJ_â$‰*@17Ñ`&¥)„SÒ” 0ÿ.ŸÖqWêðosˆˆj_›mùî×EXÈÀÚ–©i#%ÇÐ?¼À1$êEu@ Й¶o;k¿­«ñ®”Ö¾¦W[ªìLi1­æ’K@Pt’Tƒ»-/èô†Éêìè~¶±3åNÙT2Ã>”w‰Ëd‡iÁÙG>kŠŒ'7mD‰ qÈ©k”[ŸPï#Slað ·h©b®ÇF⥥͎IiÅÓRk6•£kvÙ°AlÔÀPÖ ÐÒ!2L÷;ýŽ­°[,rCÃp„"ÁŽÌo¯PÏÀ<56–Ðýò€¨¼Ð—8Ér…"H$c¹Núõ¡TVãM†OÙd´ÄNØB $#r""P,f6áæˆÐØ9$ƒ¨—¤0ø{Ñ¡—»‚µ¡Tcç=D¢°ip‡·ÛÞŠb‡pûày@n‡†!Ö+óQUoÛ´H0G‹ HPWwwk³ºî̹ºéqcmÒþ¸Y ø» sQûMO<Ñ˨yãp#$°þ³˜D7I)›PS‚¡½€ñ?F#)"ô#$Â1Žtƒ˜ûˆ¼xõXdâE«PQ€Ž_qí¤R»J=¥Ÿ— 5.þ'ó™#)—TÚâý-…õàlÿYï÷°Ú<ñ0¬&MMvfÕ´Ç}‚ƒÁ’ĦdDìíCi"4š]²…ä|r ÍÁiM^̓\±Äp¤ Ðj$§ AF=#)àØ(ðÖ‘ÊÞѬ'hŽbäÁbF#$Œx~qçË\’‹;@J!¨ÑÚ~ r#$ýäSPù«åñ -[TÈÞIs#SÎzúýå~²Ö¥¡%¬mŠ5hÚlc ±šfT•¤Qd„#%2$B”ÂÑ> »Ô`xçYª|»D·RžÍ½]ëŠb¯èêvÅCéÈáê|ß(% Âè±n6ˆ,–"11Ž³XÞZ V£´‰`v´ B܃æÞ½-¬Âü)ÝE¯­ÚOå»'yw;J{Se2†OÛtZPS#Dµ~@íÓhŒ“¬I³¿—'*ÈVa’`AöÕE3Pj¹3tP[þ#Š‚°ÅŽ´"È£ÜB9zÖ®UÒ·Ö·²õoDm™J´dÆ-™JY±L¸Â´¸fh„hÆ#Ÿ¼ã)\KDˆs›ã#)Ÿž·nÄ#)wªbF0pѪ¦µA’i0`ë†^­a¬pÌÇÞCÓW{Ä´¡‹ž&A7¶¨·š^Î57šL¶íÇÀ».]¡¶A¨ã*¯%;joE+]ÝLÇ%‰Ü\"µ׉£¬†þ„`Kmf 1äÎ"ãÊŽÑ1¤Ò¯`ºD²b+®—ùç½îBx§„NӻĦä0cMŒe`JD˜S ˆ«î#%$ºà]iûbÚÊQXKÀßglrƒLÎ),1Çð€‡¯/*«UZzÄÌÖjOŽ"‰ƒl I&„`Òð&RWáP6šµMêl!y¬êjZÂ’¦kïr0µ´ëíq&¡8'7[‘§ËÛ¥ŒíIðφ%5†ì´Xh4Å›µ7¢±A”2—ª2˜°kh¶––v‡„OðØ%¤äÎØ"WUè™Ôéc=ÎuÁKNl°‡ns׊š5’š–ô`<ppù’2ñ9‹nŸsVZbÓ‹ýøN,‡[1ºmÇ=N $PŽ¦Æ«iS‚4ÙÄÁ™œ,”ïœ åðj.-£D"ehåd9È›U7E4¥šÃZç<+†¸AÆ xÔÔkGk"ju´Ú,ë–r· Ð„ û1A!,aAÑ!#).Ù#eŒBŠ1Û«±†!À#%\ÁK#%#$p[!##)aTÃ#)`¹”@Ì°8_zj– € I HY¹Â36q¹–€žýošŠÐ?Ä@¤ˆ»Åvn .^žÏ=9T%þÜÉŸz¹M8¤Qb{(=GùºPaù ë«#%®yøeCb¾4v6Épì#)ØËíµ¼×†€(ôÈ0‡á­Wœö¦&„³Põ™õ²EÉ $Œ‘Š/$ŠÃýV#$´mdª±jÒjÕ~Ų¤’$!CÜH|âYmq•U–èð™LK‘ùŸ õEJ¾ÃûßT¾œ_¾%’ÂÍZþø6óDëªwŽ«Fö¹”Æ«¹6Mìé§#%gy“LÉ•sS#£*T»§–Z̧XUŠl¦Â&Èd­±v5-YÖ®”¢&†ClC8MÒ.™{¼æmÁ鮊£’ÝJ™“I)Ò˜JQ‡§—jx+~—K\‘¢LSI”QåÃRlˆ›™.‡Ð8¤Føáæ¨D]Ê4c:µ:ßcŒuhÖ꼧¥sù–ÐØ¿ÌäÉÆòmN·äLÌ[űºÁH¤;,+§ÌR)ègC1ÒuĘ•pìÎݵfgiÕ·Šªo|V6Þ¦Ó\$×3’¾fŸl¥9¯¼¾1¶\d“%aÌdž9YàhQ´î›&ßg¬œžk“SÅÀ£D1ægîa`ø¡`»Ea¹9˜¾DzÙºR¡Aa%D" ÅŒDqX0õãì3„-fÖ°Ø9r¾ëJãÝâ¹^e5½—žW²­yyÑFºÜÖÓ­Ú³M-"·3 9%РJq‹"í†Y#)³±Ia†á×ÿ +8ô4Ã*ˆ•ª¾˜ù\º¶í c³tÛ9Öös®]L»Žºë‰×qKÊ&·Ji²u«´šÁ¹]×nî)4ZóºQ2Ó´÷uÞe¯![Œ¶ånÓQhÒU,ʳ<T‰¢›FÍH#%$‚Œ¦´Íd”™eclZš‘L×Sk¥ie-2kJeFZ¨Ó쾿<&¢¬Ê ™¶­ Qµš‰ Œ&òÞ¿1“È\ÔyÂÚhLÔ:˜€‡ÀØ@“ì²PD´MÆöÑ‚—bQ”T¨*¤ƒÇ!¥7Á|ÐMÄCÎvž»£,' „aØõiúR÷– 6S0Ì×Ømú2Z£ï"h;ÝíQ! IÝø [0‚ÀZM&p2ð‚Xwç¦f6¦·è4u=F-%#)6=S½æ÷§L’G€3ãÔÄ70ϲ[”àÇaúŸ¾Ê¯To^‘$V¿#)-êD.\ ƒo†fPV’Û9¼n©å”kÙŒ&E39þ™çPGña² ±¶Ÿ”]"X+©²Lãu²R“ª¸ZÔÊdWÂr$#)‚‚f‰7áÓãüœ²tv°í#À6ðàe }z.ºtvƒ¹“˜ü¤ôàÞûp^h½æ€RZ+V»KÄnQú¹˜,æÈ^q`Zd!æ €ckb$—fw?Cd6B]«{ƒìP¹«ààL7¯¹Hg\-˜ŽUmÿmá1£ÐÄ)ó);~‡wn°qc³`R×Ox?J?®@#)¤U"¬(øz>D+ öO3VñtNê²Ê(÷ß Š5õ€Ä¿ÙÅM *#)¤#$¿<È´LPPÉj""‹hÓ4J“ÞÝTD’Mã[\Ðf¢VIŠÞn«\šm6‰Ki6b©4Ë&šd¥c1M5 Leù[¬2Õ”ÈlÔ24Ä–Š6Õ+TÑ{ÜQ´ÖÊîéjïVë]«ã·bŠ˜Œ2ÛS$µ4Ú¢¬A©£6ßЭ·Uö9 $ÙXÙ+kl’DÛ5¶¾WjZ4f©&¦¬yçxf’Zm6Q™SVV[+|;kl¶Æ#Dx·E¦ËyÕ×É)U1B¹]YªMàºËx×TdÄÖК׀–(D +îpåø8«•5æ{q¥B°CF;÷p–#$ýege ÇvGËÜuJóiÓ®ñ6û¤ÐLct'X²L!·zú·E¸h%gÇĶZÑ‘ª êIh«”ç åõðÑ&­ð¾U­o›m>{¦jZZYš[EjRš©_g—_ä~ŽµùÛål„F¬Ò¶R­ð…h‚Œ‰¾S‰ 4•!Quª5å*ºlUcnXª5mwßí¥°`tª&tÙM¥#)&*DSMM\Õ¶,•ilÔlÅ,fdµ~;JèÂQ©IjSj´Û)–²VÍ)¢•&¯­nPÉ%¨Öm‰h&)F”¢hm6ÊSHš“4YL³E±Q•²ŠXÙ2&‘¡FÉVJ©JÁQR›*J&Ù6¢“l†,‘¶Q5F¬ÚiR$É‹$¤Âi’i’ŠYª›c¢J²$mE,Lµ“²«RÊÉ&”É©Kl³j¤­ªújµÝjÒ³kM¤³i,–úJÕtÙ³Z¥-­%¶¨µ|µy³T[y›m¶²–Å|*®kjD”TE I%ÿ4ò#%Üᆽä.˜»°µíx:’¶½°†[2wë2íuÃÇ@ý#%`=S»Uá„¢fxbn*Èl""w·m NºT_yí#%Ïw²QÆ„=ÞTÝ0Q>X8y¯Ü"³ #í‰åõá€yC|vGªX÷· rY¯Ïã»êÙ×]ضh ÛYA3’ìõ]•Mù”£v”<<‚*R»LK¥òK>SËŽ¡רhÃß›ìÁÙ×t¡_¼ÔbȲ,ÒÆ6†ÈhHôž‘ÌdqÙ$²ú^@À¥7ÍíÏתüSñ#$x,¯ÐZÁ<-ÑA`}@/‘©³\#%Ôs½l#%°L>Œñä× ý9*`ˆ¥¿7B SÇ>0:³)ø!ˆ¸ÊYeiúß?ÞØ¢E™ ‹1š"„$:|1z÷ä³<õ°Ç¡¾;3˳kÃËv;oªÞ»§¸¬C'~M=xæ#%&ýÝø5WÇÇݲÖñg¨D´Œ_J‡ÕRÀ`ü(Ñ-iû¥[¨c¥ ÷í8m²@¬’3¦õRiP:Ù;$$¿¤H­Ýú:ª9¶…¹MFmiœû#)£#%h©¡#% ËÚ—Îï(FÂí" Öæ«ú>—Öù÷mñÚpb¤]À#$±¶ÆªŒßj®ëªä=œ>[îÖµw˜ÎÌTA÷Hø{`ºS«Iî“Û’’¦ Ÿ7ÑÇ.g¾ÂHƒ#$Y6<JQéà(Ón‰H8†uì#%°×xž÷áöŽ$ÖÚõéZŸ3|§ùz6möŠ#$yfK¦ÿ»µt8uyÝ‹ëp<ÕÕß#%± ³ûPÑ9‘Eqȳ•¬œç\ 3€·OîþWªëºéÑW7"þ¾y'ñÞ|;s^¬F”+Ίºhó»å¿¢o\ßa&P¨’ŒkrÈÀFa… ¨ÁV*ô4 É&j¢Àm&1íÖ‚ ,¡5#S 4?Ü£ËjZ@ÍÓ‰0ç"­²2 vÚHFsba‘#%‰«+Ì”fGM±ªÑkDª¨,Š×K¤& ÐðH°Â¸ý†ø¥[ô©5b0k ±Òôï £Âg+Ѥ£À$}ê›le#$Òб™èÈì#$q#%‘ïC~ú Ì‹D0•¡1¦ª&*Å\,vÜøÀM°HbІ}±çÏVú}kͤìÀ™3`µÙÚ¤áɧoFܲKé/Ûbñoe¨ @AVÅBúf‡WI}Ú‡ñíVÐ0†¬î–"CÞÑó7(p€#$MŒ8‹*²üÌ-@÷æîòœù,`I$c)Ó4 mýÃð˜0ƒˆ«àPù¤I#)\‡\iª¦ùåç6…zm¨þN·‹¶ NlÓx e¨k/´œXgÏcªH¹P!Vúñä ¾yðûð÷îcÂfô#áÏØ©#)g3ÕéY›h–½VIƒ·À˜2õø;3l’vÓú‰cI?\(Á7zôï>(a™®„3U Õ‚ÂR È_Ó.N z³ÉÔ S#M)L7E– É#±!b„µ#$Ø4Õ;Ô{õY#%#%Âj"…ŽAQƒ2Z=ó8#%ö§kN¬ôë­UÈfTéÖ-Â!k–FÉ#)/jPxG"¹ÖQækX#%§£13Õ®ÔìH¡fŸ<Ðä©!aDáøQT$DMšæàÜßJÐíSõ¿dUA©çÄ1í³˜¨¤ùµ  ÏÄP)ç6åç3Þ¦}dA0I¡x6UÐ[ÝB¬SIDÐã¤Åõ 2­&‚¨1Ú‘m.Ïæ¾òáM¯yIeèkA—#)ò##$®|~‚6€CÀ¥46{Î$Q4†I´XW'»Š±5‹Oßçp¼ém<Zq«ŸÖj#F¢/…2h†7ÛCVˆíÈlùò5N‡;s÷bݺ28srYŒPÈá_NÈ™Újè5îËU†¡´Þ¶#$Lˆ`#$àwøÔ“—5V+ꢕx=eê,Èõw3»¬ÞVÌóT.4÷OLœC|‡3 .¥ØÆèÖæf¥ÚÔí]v®øìpEð]á‡ÈíM-H“ÀÀÊåŒÈªâż²Æ>õGvÒEhïTÙÐm(‰rI#)N##$($'N%”‘d‚ÅuªbÆbŠîC¨Ýw}låÇŧ 1½î>k£cVBI‰­6‡¬;©Ë·cgeÈr_U×]Ü‚_SKó÷ÒxÕ¢REM*u&ÌÀÁA¤•Y¥Ã½îIB&ní:á!GuàêèµÉJ\éh9<xÏà7¹ <®®æU9Ýùß-ç±ÞëºTF"pª?UC)l3@7–ò“õF#)Õñ0±áÄÅâžOZèwg¥áÊKQâùRÚ«­ðËröˆöÛl7cÝ99öV{Mì'#h6—Dk»\xÈØKã}ÃÆVÙ‘ÉÂ,‰°îÁ,~ÏRa=9‡#$8'#%¾>|ºDª#rø‡Âó£»Ù‘Ò“¹êÚ'W¸ÈCP=x"p/ŒaŽ(Ø0  ;ðQíER]SY2s9õñFm&wT0eÄâ\´®F zž,EIiïmEÝ×I_]‘!”HNóÊøÕ{«´›µmµô»kz‹ckWžÏå<];«©’®\Å/„ËÌÖóM×–æ­²Ë(F(k‰‰˜0> ò.CoVZ®¼3!ñúvñÎ* ÅC±#%#%,Iéá9õgr¥Æšá»«"vP1AÒ¹“oÓ=^ÒÌ‚ÍÁëêòèkp¯խ:4jȸoûìÁXsko‰¾“Êü­­½TVú6iŠÄ•U\ŒÛ,Òi²ºè¹PÎ릕ØÛ~vÛ‘"´V½¥ÛÕêömkÆå©fßÊþOxµ’µ¦Ù­­/R]϶óYp®h“ Úþ|Sše>üia2뵙ĆP- ²—C€ËN&3¦ÚéŠ{àb‘Éç{…Ìžƒ\Iã6Y-U€tÁX‡`—h‰‡÷cïÁ·dfqаj,#êÞË`ï7›Î#ë’Š†j¯÷wê-)C–ú*Ž’@<ÉUGÍTª2ÕSÒŠ#)@œÂæ—#)rVl5dóTy¸}ÞTbŸ§mU5¨¤H•E8´ÈÔi+bÓ¶­º«\>m{I±FWU— a¶V™3‹$æG›‘î¨p8mç#$ˆ$9Ä3 ¬"[ËÛ}ç¤&ݤß)Tå © ü’’"s,XÚSÈ2O¿oq‰–^}?õ°ÀEÚ經`d Œ–™ =ß?¸åœ’B#$#$¤#$J?Ë@oû-Š¸›úìû-Ý,gÒs&É!È„‚. óò4÷lÖ&&’/‰£‡ñây“å¤ú²Ýk]èŽ_÷Zù¢P3Jˆã´3檆üelXrÌ1¸y§ÜࢬวðîøˆàE@ôâcé¬8‡Pë*Å]¡C×9ãâd묆ýÝ!y´Ù¿ò|¯gPïh"3ï ‚ps6)–øSTSb/9–>å~3Ð…þ3¸‡_œCÌA$N ¤ªF¢#)AUE„#)Š•T†HïFŘ‚TP‘dH¤šHT¤Î¨z_qQ°»»ÑýAWµÅ[bêT2€b:‰B”gã#$ÜÈ"bvœÓ™ôØõ=j~O·hrا#)òSóBA‰¹<éëJ4zBrN&¨ò³‰Xÿ8œ9‡DŠ‚’#)@ì„;lí-î0ÇÜ’Õ"ËaH¨«$+ì;¾¶y'Rf³LŒ&8÷vi¬Ðh%èr–X%nxs‰tP?ié=S”k¿Wq@GP̳1^Ò€#)CÜ:»øž‘ûÖ0ˆwP;1a9ªFdüãSïæ #)~©TX&õ"ŸÂÃö5¦hþEû5©†:T‹BªšíÅ4T2H’©IK#$Icº™!—íCF,Š:×ü¦30è¨N-6 £D6rÅâÄ&Ö1NáuËÖ‰0Š±c›ãƒ @Ùh)/“͸ïz.´(h…2KH´PO¨4ÆÅ\Vi1Ò#%Œ j¬±20Ì®lV5#ÓÆ6«Cm;µ¹j6cë[ϧjõmoDKn47x‹bbo†SQ#]AÛ_-­Í· øo&«ÆÏŽ×΢A’b(Æìæœ0B ÎQ‹LÈè––°[(- :”n™W5š\¸Á¶EXVÖ¢ÄÞªÖE&]#%TË-8Ua,–"‹XÓH)±sj(Ù6åÍsQX×1&5çÏzôÈöëµ¾¢éo=ís8“;H-3†­:‚­ K¨2.ÿñõ˜i–4ß%Ã#šMeGR#%éøø|¢ßjù¼âð\XònÝÚêua¤Ä´œ’S›:] ¢¦´RIÆZ±3͸´ö>€dy°áàLÌßò¸ù€ù©m²O5I‚kªnt!cê{ä;âÑ3øÍÚ¦fÍ$ZɬƼv¶ªÔÒX¡‰ÓèÄø_ù¿¡ÏÉs·¾{Êlœ‘8#Ð4#) žØþ¡ù§ÙˆX„R5pEé#4ÿ4}ä­iÜTny[&¦ÌdÔY¶í¶2WW$ÉQ-Œ4V(¹«n¥cV1m¹»[ ‘ 1#) 0Fhš «#$bv:`nj ”T£Œ ý\3ôÏÂÉ¿ï@tngËðôñõ÷ciçMZ,`nqùý&kŠž:"HU¢©GöÑAhÁZb5LƒKº‹,€†K$"Q" k®¾¾ºl4JŠ:qêökMÛÅ#$ œ”ÜÄþ1ƒxª¨$ º«šMëW —ÔóM¬–ó®¦l³mÍsmâ×àV¼—®Ôlm“`Ö°Ù#%¶:V6+ªîØ«Ö #)½‰!zb ¤T¤‚¶j‚B4ÄÂ’kRmT™¦¯ž–±$4T(Aº$—(<ÉŠq®ó2±û9uC©¢àE­¬MÁgr£—$ŠHØWå5E˜2Càê#$€aP3 ”™ƒbL²,–0â¬-#$/}EA¦½RÉÁf#$!´#$/7ÊS RÀÀÄ/HpÑìtÞ8£”‰Ö6) XObûd‚†¡’%ÇŸq‘ãßNÂðM@DÂQï!D*ô‰UR&AWC;ìÙ% ƒ¥#)'£° Fî\I¬ã·…Ë(™†üxå\ÓϦ#)T?†ÂÊ#$(¿€|O§”MîW1 ª8tîØ`!ã$úÃȯXÈ[MIT ˆ)m)SÚ-C-5ó5Ú4Ö³JÊ{+¨ÚÄÍDQ$¨…üðtñç<ü»}]¢ŽPì·fûZ¥Š `mnŒŸOŒš§•³Õ”8èQùÇý~v ö£9·æÍ¥¨4¨aéíŒWšêàÖÛÓÎwgë•yL¼óÆ‹­ò‰­êÈo¾wÓ"tÚ¯y#`”PP]•Œ6ÀÞåA im•"p£è` rÁ“ˆQ&·‰*†ÀYˆâ=ë¿Ø žOgX ¨’D’f'DTGôFæ.'.íŒÛë¾Påó:‹ÐNfa!H@ÏzO ‚A;Q+BCl1š!7aJ(ÈB˜“üßo q“„“gGA7ï†õ3Ãu%\›9¦#õØàQ€à› ²'‡­JŽ–´ª*©ZQ¥Òé©NÖlÃBFk‚91o³mx6Â#%#)1ûÙ™&Å0Ù#%9$¹Š„›b JD3¾JR™Cî#$õiÇù²v?oq¤ôf†fâ»eŠ>Ž(q±é–9lQ³ýРøü¼mšãj'ü?&›|.ö%áršƒEζö™œµË3ÚQ•ŒªO¢²uuó§¤=Oµ4Íz+†¦*f“‚F¡Z¶0B(A‹Lkl ÃT””›4–ÙkMRlkFF3M~‰]&‰*#%K? m®j¦eSf6F3I´’¬Ú˜–-e[+6²”Õ“kÄZÅ¥lÚ™K5©¢J¥b­±´‘¤’HÅ"ÌÏÈóQù©„Oö?;IÇv²[ Ò3QÁS¦!Ãö¥€5$Kk©ÊÕFÔmQk©m«•»7ƒo0¨Áoêµ-z­uo¿P:‡™ÛÓ¿!#%ê ²b5©Q$E¨F2]KóêoŠÛõVoãÒ«íYü¥è$”WѶ‚.%PÆURت;¢T!cÉ sÝcŹ>% îÞò°0\#):÷m¼HV|Õ#$îÀSÍßýŽ}xÅd#$¢QÕî ôÜ'¥9gö±RýD„ zb–ˆ"T‰öÀ¹KÚ–é#$Šñ¡},ñ ÙÊñ-Œ£Ðð=ÙcýYí7mÖåì#$”W#)ÉÄFy³ÊêVšÛ< ,Ö2¶ÓKÉ“'°ý²¸ ƒ—ä[ 2à–Bn>8Ûðõ*žr'`ò0d‹â3¨h…pvª›Q3Oy°»½x==@dï î IY °‹ aP&­É<yIc_pqCƒˆJGÝø®˜rÏñ½ØO8ŠÖ0† «#% $ŽâPþ <ÃGNB¦ 0¤ Þñ?G¸ì,ù¢l,{Pûÿãûã–•ŸN³k#%4GUÔ/ѯ¶)údû{Aé¸Ëj6JÝÄ㌢u1%z¶Œ‚$ˆ`é‹cþžGF+D/âׇñ»Cñ^m<è„!âµÈÚe]ͶÉ3’_oÜh¡¶ êó¾-ApÐHã0´EM4ÛÈB û0‰¤ –|<#%1×jÔ>Nòý.’(d($EQ '‚VW-qŒ Ÿ³—­ò¹½ 9>oôÌhÔ#$~ŽáHå)R¥,¦üÔZúu¾|óÎçi‘qº#)¡,¢‘‹íöÐcÒ—ñ³ DP8#†¡¼ÜÕ†c€ÛexÀÆV#$0`"BaKSrÂbB&²Z°ø\73±íèÞo˜lip[}[h —6ÉiCj11 C§#)gï(ÒDL6јÂD‰jP1Ê€#%à žó!vBèïøçËú¿&×n{KÕ%‚å3‘«…ïaÖóxÐÚäÊ„&Ö#%G£Ã±sxFÊ®ÉØuhA × 6mÔÍ/–o&ˆQOñŒïÓ¤% î›(XÂ(xÐ4E,„BD¦„ƒ)8´p(EêDñ ¢z<¼)y\UG!Ôîñ2P1êæ‡Âr󇧶¨X²,ÐЪ`T¶oša-]Ôž´üS.ÐÊ ç²ÊhmÐ,‘¨%`÷tÑè1Ü?PÌÞà®øöÃ,È€ö’CÌ!í&C6ä#)ˆD@YhR# ÆSQA†ØFþ™ø>2_}m÷ÛQA_™ºU+ÍÛ[¨­úÞW…ñ¾heãÇI¼¹’HJ»¸¬éHˆÁÅTU-$äé¨_* Š¸‘Ãè*…"­7k *#%¦â7¥ÚUŒß2Í0dÍÖÕµ­¤âqrcš¦\RA„%G¢#tÃìż8اfÓ½]€ÊƃXcÙâw4ÎÆZgwöÝÄ5»¹U÷£è¤_vUcŽ††Ú 14®\¨#‘ l6õ¥„æo!©€Ÿ$‚Dœ”/ïHp¿>ãŠÝ!b=÷;’Ÿ†þÞrk#%Â_SýsÕžCl ÁãÀ '½²æàÈ[FR(ÚbÛ&®Jª0 uV- £ºÜLî®Ê¨DÄ•Êæxè\hDM¸åFx|3!ÍM¢pÑWDó×(£W—“9©×..NÏ8#%Ÿi é)bSõçnê0†ßO#)ÔêrÊtq/–÷†6äOˆ…§WóU¦®äLÙÌÓL|3’øàÅË ÏR¨œíÅòÖ³¨%®¿Ùý©ë·íë–r•:è ¶lo.7ª¡X$QnÁ¢† RZK-…²å£ Aæa€6U¸¥Ž”È)ì"¶"¦ŠÍþ‰Õõ"+ÈÞmÝCàAÝþM v`­@&>¬=šmÉàt`Øé`¡‰¿«Ò\8Á„N J:éܨC8§^Ì#)Ì($«U‰`ͽ­‚¨ï(ÙCGåË£—Ÿ@2Û´êÏ"l í·š!Ðû<ÎO2'£»XÞCO3Z®Ý{Ûz`K"[KÎÜDal… Ø¥q"!·b#$Lb@ hÄÈèÁ¸Ò R"*©T!`¡#) $>?73™ÐQAl Gûi½•ªAßÄA°&Ô›£jeUT@HO¬þ«lþßzÀ­ŒCÅhŸa+Zi,j±kP‰¡ös=å¯áÔ)·w¶õÔ¬cº.™p¸±ªÛ@‡ÚŒ¦y—Š2m¯.w6M}cÖ·¯€œÝ­d›q ŒN§+#%b#dd1ÖŽÐ\Ô-4ÄÑË1éBkTÅY¨¬!²«›dÓÈK¼¦y¶FÖ (¤$™8 pÖ!—jÐ#Ca†·ÃÐn,ªˆW!wD 0ðb¶[0 eö¯¢#$ÿ!ÁÂø™„„Œ#©T2ÛR4X7©Û¿Ê±• Yü¤péóU6u,<ÞaÁ‘Ý«éBöàM“þ÷cÖÈiŽ'D÷/Tôõ B0 $ˆHã#)b‘e¤­™hÙ-%[¥5_¡_¥·ëµ¤Êë¶ÙÝÌ[’Ô#)Q „CéëNÀÒƒäÜ1ù˜s…–B‹›¹¶©ãñ[€E‡—e¬Õ¨{fáÔqá¸ë2öc#$W"Ÿצ{Ê:š?†'R&A.ìèskÒœ Þê<³ˆUåŸôZï­o6Ú58U~÷è²- ²ó|'ÂÃRy-o£5¸Úa$Ù6UoØò™PÐþÛ×Òc†þó{¾2}H`¯”M@æ ´¥ê¢6`C°ðÌ:æƒ./‰dÏFg¤É„‰!2dÛcéÓs±‹g¢³|Sòf²f̓Xf­0ìnd«#%‚VÜÓ~·#$Å´S-3JjÑäJøKºÆ­žœùHÉ ø¼±ÜÛ‹n¿œÎÔp9ëà–«°ÉÒ.ÞGj¦4&zýrøŸÝ뇣ma±/ŸìŽaSYëÌíðõâyÄ1àŠ³ŒáqÙ´ôöýW›'c#)‡™¨Rß\ °4bbU™B˜æ+ÌJn.tþTØÌúQÉðLikâ†'XÏkW®HâuYaÔSZ·°òü­#%¥÷ÁYM]¹úŒa;ê¢IîI3“§ëÆüç_^ÎOr—»RýbPëð5d†jØŽ¬§›”BáEA4¶tCCsþÍsíØ4d³»\ÀçÑ}0OF#Ê[ã‹és—µ‰ ³©ÚÑF±‰Ø#%5"+URT‹ˆ¿qRP!Ø/žøîå´#$A~GtºOúÿ¤¿,+(ˆ ධº%ÄŒ›º²Ø\º¶®éGƒ]Yçnçnçwµ+mL­oU«¦ÛWîM¬•ë_©&Ä!41¥BûMë‘s¸ééätãàzˆó8¹(’L>oßþ| ;p€äš´¶t’¤4€#ìÚžü’ ñèÙË–MÓ7Íys í®=l ½Äìsn[ˆ9à"mQ$ sÂý¬+4æhë]["tÉ3‡Bƒ(u#)cE-âj<„–«b/Ôlweõ0` í¿fÄ!lAÔý¢uü¸š#,vú~BDV_Ïa‡`568÷Câö-QU ÉŠ”œŠ·kòÞ5uT6îµuËrì[«¬µš[M-hµ))fm­™j®«cEr™x¹&•Ö­öKB$# ŒQFDE‘A9ØÎl#)x ø?WÜÐÓ_DS®©–Ù‘ÃRØ ¿™ dÛ»à–NÐùTlmp0q¾¾ûVc…„Çð  Ìý< Ànj‚˜ârÚ¡–oÍ 5P¦o°øDFŸhˆ24Ž2$t3i0Æ°°à=9ý-YØÄŠY )AŒiò㾃§óî}vÁQÃc÷“nÒŒM¹®7ì#%Áu,ëÁî7)‹+X6h^¦oöWY·QïD §ë¯1°C/J1n‘âtzv-¼ÚÊøü,3<"#$iD—7#%cH:V•µ‹t¼õ+{ã âV#1Er•¾Õ#)OÓt“\˜Æõk¨ŒÀÇêMb“d`ÛúËæÂ!±…™¿lOù?Ëþú_î7ëP§ó!&R¤â¨Ö9³æ¹”‹âÅ‚F#)JÒDøvÒZ¤(M¢9ã¥][¹”4”…H­±0¼d#)CôšËÞÐètÉ£ôµ.Ǫj™HW’6Þƒ`#%H(3€Ìv-8x$Å;`RÁñ“¡Ã–×$PÂÜûÛ¡‹G‘"`÷½#%‡Nˆ¡’Ëô¾IÇ‹äGMé«c–äP5]Ô@}ÿ4ÿˆYäýÛ) $y”pùUÚþÇ®8èg*«ß&ᤎ§f¸#%äxñëW»ƒù_¢ÈÖñp™³ ÍÕ˜o“…ñ =Lo¦#$ö ¡UïÌ«<ž6i´ؽ¶#%Çðb0ˆÃ¼;àÎåéÒ(íÒi›€¡æ]&#×ÉI…ò#)>ê¬êIœëPù²– zr\Ckùƒënwç²Q—d§÷Ö #)n¬Æ:×i¿LPt5;ÂCPÂ)´ÏU†ŒÛ©™¦©²[Êcî­ŠÑç½JèXÊ8ýO»CbXFä™a¹÷¬#̽BΚ>}#°åéXj3û#!CrjxìíÔÖ08Ø+KV{ÜLèO53ÍŸ ô™á€†nEåòݾz¿£1HŸ€‚1 à„•3; döt! #$˧[¦qŠ¤’ÔT‰Æ,–X¶¨µmFÛli-E©–Ô›XÚÕã–ª6×-Vû)Œ.ÇSC»èÓ—EÊåQ¤¹–¤±P#)„?6rªöGq±$ƒÍÓ´àv6ßvþyab& ™<Ì|L‡_ÌÔ f*‰#%Už}¸}'6˜(eõQ·fØMj~‹ðóã>Šcâ×M] ÈoåµM¶H0I#%±¦7C·ZAŽŽa8w'åæSÆ@Ùâz2¿Ùûý` ?D@ª¦‡_>ýÎ#$ÍÔ(”“¡AÌÀ‡xRW€ßO,ÓÀº?vƒ»_¨LÆ‘î8ØD ÜÀæûÜæ'²ăõ3Ž]ûi¾¢1Ô=4=•¸¨ wÄ0ÌV1yɳ”dH{³wÅ‘KB}éªm»J-ÿ÷¶Ú`”&ùÔ‘X|‡Ù z‚r¹6gá#$û ‚“Õ¹#p>Û_&ãßÝW¯q»‘zÊ)#%)¨9D¼@è€<-ÓT€kã1°¾Ž· Ò¢ GGFÈDY?Ò!h(îNçA8Öý]-{¥ë.„6¢ [n#9†ðÄŒCdPÁ4RÕ#)€ç¼=þ2Üác¾W_n§ìµBV‹—HÝŠÐPÊ®îÛ®.ìÞëê ¦2ZßE¬ù3èŸN³ ã-O%j½þCèÙ£Õé6ù$|E¯¬í—¡öÓáù2d|•´±<gíýø\6x‘KßdÚÙ§ZpÈïÜÎ^1ç\K€ æ~»!±qÀ±aMU<ˆô©2˯môŸñÛNºíÔó}~Ñ>¿€^û¸#% ÀótM¼: (¯Qí ®Ï^ÂòßÆm¡ò×#)Uy¥¶Â‘U„Êæ¶Õb#H´0~C€#8e+ÒM&]LŽ’—DJ¨‘ÅP Š#%j¶¬ŠBŠIÁA¦’$&›R™›FÂÆ£uÝEz uKùžñà"Âë lÛ,بZ18hÀdh–Læ†Ê`”Ð1SKC›»#Nn$“0U5$2H¢cZh%`~jA¤067ï͈Y=?l¶%]à)‚I´j¶Mt«›ì•ÑfÉ«IW:IyÝ»¶TšXÙUÂìKë&Éœ°B1`âT(HªYE&j…™Î!l‘ˆ1Q”ÒË=I^6ò¥¦Ê™LP#%F¦ÖR±,3LZfÙ$™´Ø¦³2L5²–—½Ûۭɧw9·%E7]\«”í×|]x×›Îð½»\¤Tª‹zö÷³a“LÔ<¿Œ^qóÛä¼ÉqT UÌÞ˜(fÓK0 ˆÅZx9³YV€A nºG‰éêÌcuÛ¤Ò&Óµ6;•VŽ#%¾ÊŠñ„‹#Jíq[DŸ.<˜g‹o`ØP7ÜŒ4×b¦M¡»âe,ÚAí³°© ²EX¶¯mt‹·ÓºVjK_Et™ 4æÌË%ªÅ¶ó‹¤l”ÐŒJEÊÐP2 ÁŠÖnd‰F;\4d2RP3f´ŒB‘X¬4Ô*Q˜b’#Q±4 oqm,cJÙi-+BA)#)ês6ÁŒlI» ˜ÐÆf;jc ß».q Â5àb·!YY…¶ dâÒ«¡×#Ya ‰¥Z &LW=Ù¶²C$_S#Ó>ûL“aB4SØà£Ö‚&ì¡S<¹Ë†ßjjá4:éTWGˆ'ÊÚ™±˜ÃÓÒcz@­‹ºáT¬)™5;›f[:¢á¤´ñ1 -bÓ6ÞÞªƒ¢òÄ*ÀÀx3éÈ‘ò5Î#%F.ÂýÆê#%šnX¯h‘Ÿsù0|äøžKeÎÓb6LÌ4Ć…·§1$Ê æªWz%I]Ä#%¥¶äDdbŒå‹$1è!f•O#1E“#l3&m’XªZi½d[Öõ ¡G·— ´&µ¥Y¶iSYpŠÇ-Š”/’ÔѾÁÃÁÆѾ7möötÖpÀdz m0¡ºe×zU¸JEï#%3¡¥¦!ê$åJÜ,eeU»côt!;ãÞ!`…Á#)"¤ÀÂÒˆ‚ Hij !° Ð&•" Àa€LAŠ#%Œ ÑHQ,F#%’Š¥Š.ä˜%ÈI.%þ ˆÙ¿‡OQç¨êl#$kÓóã=—j]›§êx3³qõ÷5‚Õ‡û±bÂe4~©.0¦*àû>Lh™5ë[ƒÖ¾·J¨MŸánšÒ[~Ÿ×¹âí…μZóÎÍ"ooOLzÅsÝ4<5¢àݶèÓo=ûº™)doowIÙœ„+=uG«8‹¬ºÖjcfgME,«8ÕÅ™)ÃĽþ‰¥dõë]ëöÎïc𠌼@„9Qy·'Q³yÑ]sÇ™· 0ΟDð›ìldêïß3\p6´°+ C“)ßpxÜ2©´°›"Hº®mÊ·{óO†í¹ûûŒA¿=EÑA¢vÚÁ$yy«@Í4bK~²‹è}xb|¡ÇYR:{ŸŸVvuè›÷"åÃÀ{³P’X§„ç#$ïŒ<ৢf›²¢©–€[l¹$‚Bļ.ˆ«µ² j€6… 9Ø&n}”Rô#)‘±³˜ï±m_u8eh&—®8‡ú’@yÂ"yþ™‡—8GÎ[Å$$,m„ª[}uü;Q¶Ö|Lºo,ÒÜÍ>xÈ š6D¾•VÈš6%ËÁ¾Z£’HE#i#%Õ]ZñÒÆñ­Ù«p¶Ü¶ÉUÞ:6*Ư:åÓWVÜ«»s!—/QN¦’Ťh¤ceM¹Â§vÚ5IYTÞ¶Ûů6´›¦µÛuçž1hÆÖÕ‰6€À;#†DV-™WÆÅF7^™c"“>NgÇ ¬‹i$V#%#$™D"…E… ÓX%¬ O<‘Bê\#)B.øÙa˜”¬—Isr´×Æד4Konµ^Ô …`CÞòß&Y¬VR5$D2íQ-oº™ªIüÇUÙElVA2”±¨Ö4TÈÔZ*-¢±Ze`±ˆ¦Tl›e$ÌeS`©™,cUM^íÐHq©¤#%Ç·§Cቊ|zþ#%BHÍÝ÷µV½øƒIm¡4d¨eS¼OŽÇjÜ™#ßÌàáýÛ”0¨ê Ä¡ÃeLi´qzûN0PDŒ€š:ÒS-Eù—<”Ùbªý[òµ­Â­xÚ麨¦yºèÕ;»tµÝ¤¥¶¹m£&±[º»JV´Ö²½íÙ¬©Ab€ ÉRŠŸÜ§`qÅOÐ1:#$~{)_²#%!¬C˜š°&1TP#Å!¡UÛ€#i$€H($s£{jRÜutp h”ƒ…Ò»°§0Cl¡‘pé꧓Eã!ªÚ8yÃÔ\J~Òo=Ý•ö<@öà£u0}2/ÞùQXA Ä» ªÑ„È‹éšnBæ;Ä|7pDÿ®¸™·7²™˜Ìn~[)$Æ2»™&š„Á4‘3ifÔÓ¤‚M0Ì–çjmöv™MÊ[]ãí+™ú÷8òï9„›YxLh&!Ð&;dé¶*ßÑ=˜ãTî°…#%l‡2}¥t—#%˜U{FK%j¼¿W®¸9ÂérAs߃f›‡É„Ê´²ŽxÎMË°²Ø®‰h¨8¢iÜhp³9¢¡†˜—w³ÜðtX*þòvЬDBt`,:ÎÌvîVCêì7^W±‘Sj›'-Ÿ³OËƇ÷V=0TLF>=f’åLáZUI ½dï|È|Âê Ì2Tãø@FAQP„W@‡DB "°Pÿ@Ïë4ÅI ¢–:º­äžº‹dÚÖ5hÛrÖ×€äqóŠ ûBXýáÔ9ÇÃÃ’2ý0$#$b@ ÇeÌ}Å!ñgGîLÞ` P…JÉm5×gÇ-´¦ÛtÛ^Äñ(#r4Dh‰xa"àÚ´4}ïf¶Ðˆ6IK#$J`•o¼M¶Öd˜ÅU^,ÛÕî*¼î®Äø]ãk©˘µŠ¨¼¥ï#)±²¬bŒ¬HáY¼Â±æ“µ²3(€¦`Ûe£lÕ©J˜ãih"«#%aF3ÆÐ40¡Pˆ‹*-lX#%/!•°“0ŠŸº%f!L"\˜€¼h>AüwŒ[i±8ÓH‚#)"0õ’Êy?—_)P¼æð”…âÏWD?â}’O«ÖÐa`ªsë> Þ!*ÆYõ,]‘³nÝM³ÄÁ7cÌ5%¨Û·?fü·fÐuôeU‘; 1’BB#$ݨ Š¦(@Còõ½ùcóûÉs C變†ßø‡|4ÃûàÀú!’`ücê—7côÿ@g”Ïð04ÚŠ¤¡X÷;À,Cvúï¹÷ËãN`ÇÝ#J&}¿–ËWYÏ›‡ñ:hò7züŒë}|vvØÿCV¢µUÍ 'Û}ìèúQ40¹Û¾q½Ñ:Ý$>“L‹pP#%í€Ä«#%4ØϘâ›Û#$HgÞ„ÐqÅë²6Öó;Ï\m6¢¬Ö¨©ji-8  ¶.‘JPô)`¶ 2H¢´0Z¥!²âØ ñû•?hA]"®EykÊvNøuû:‚KKä’_Ïš]¾'á¬Ù›³–C¡Ó:é|UàZàsM¯mUªËšÍƒz|Ý#$×àê?¹ÒDª©EAJ‚%R0h ÒÒ†aD,ÿ]Îê#)¨•#$1{S ¸ÿ‚Ï…Çx!²7*B-P%²HÀZˆB„_˜\°'Cò㊠$Æ‘ª—‹»Îë#.Up¼]G—w‹ÈlJrÜËÍÆÓe“I5 ¥5G·jm’±Rl`ñº.mͨÎÓy¼»q×]³è\®îÝ"¹x·%QÊio'“]§5™c@íæÛymDVÑY*±¶‹)ciå¹K3IRVy×t—NmÝ™ÓrE*w]ŽUÖ\ë66Ûb×j[sÔ¿ÂêÖßvò—U_Rz`#%† °/^ßFð|öC°·a 6r£öy¨€;”þ®ä-F C¾Š ”'k,ª/¬ÄDN)ӊꜧ´; ØCÐR#%ÄøÈ #)ñÉôǯÂÔW¢û#%žÛ_ fÂöyòîŸe—ÝmÉøöͧQìÕ涷ܖÚ5™€¶¨Êfhg"Ân|ZVYA`bÿ`ŸÑF0PÎtûxØL B$ˆ°ˆbªÐ¥¡–|µ|Í~ÍûMïÙv¯ÙªÒ×Õ˶4–›Të®î¥•‹›º×³[¤¯Ö÷y‰š‡ð «¹ b8hG§àA 9¶Fn”ó¶ßI1ïtÉ’J#%$ÇbQILL-˜áƒ(‹ñzÖÛɶºínÚlûySâ]umÚ·d¤l0Ð\4:g³~õÌL’„ø*%gLŸÝÀõãbPÃÅ`”`{Ûû&‡¦°#%¨ó#%ܪ*ïˆ,€DR 6mYMªU´«,´ß~‹À vìù Üé €¦¹ˆè«„ jFËI3TkmCmQWG‰àÀ7¡¨GqñÓ$ Œ0•kûòÚŒ­#%jš—·µñ÷@€ž9ÍmL÷W¬]ˆz"$§(©­ÊÍ_êøD‡pzF¡&ÐÐÚz}¼ïª;“qÓèî÷mÝùî‰ñ´‘ˆrB!§àKÞþÙùmÊðr^(ÁXq„±ž„#%0pl‰8z-‚¿—š.µ#%äE`*͘Ȥé{â÷ båpþ€@cÈõôE·SØ(aþÛM>]£@R’`€2H–:É-CJ‰Ç„"@äTW‡n›Xø{íz¼«Igö¡ZDh Æ0#hM£{€¨ìZ„%D!#$ „Xó›e46<Çåw•åoµ‹Å«š/r¤¥&HÀ!hKAÀT‘¿'VÅUwˆ"¦gF £à­'¸ÕâÇvvUX7ÞÖˆ²L ©"ã#R\¶7‚6ªª3ŸÐK&YdɜЬ…Ô‹¶)JU†±QŠ©D#$¥I YT°©TT nòâ™DM#%˜¦X‚r#$5)7nÜ›á4qû¬#øŽûµ#$¤ó͉ãXôC{ñù‹Âð£IdˆUP Œ¯ÍPP¬& ”j$!¸ À]/c?*v0ÜöC&½´œI"¡Ä!ÚNä'4”RHÕDU"J!PªŸº)ÐDü" œ¨+ê‹2W^ÚvÊ×-Wš#%‘¨T„VX‘Š-!3#$gŒŸ¡ù¢Î¬>g„Á!—æSþDM¯ù}Òöl¹–[Zm,M.ú¥_c[_NßWœ¿Lóºõ¯uKº Ú¦¸ºÈ6ÛrK&8(BIjº5GLÆ슕ÒÙÅÿCPÛ4´(«rx;¿è•VN#%œ5#)²òªÛmW»LW÷N…èxPé¦x#Ô£â!¬"xZ·†Ó3´<„sÀ¨'éÌO¦;¸‡Œ¨¶~j Î ›à¢n‘ü`¬Š#$ ꡦ +H¢í«P@æ¨õ„#)'TÉgç®›[÷À-ö(u]¼S[Û¼‡OëáûAÔðÐüF ¤,2?ŸÖ¿q­dû·—ç(™(!£ðS@·é‰ ßóô•ê+íõÕëƒ?"· PhžúØßòÁfÍPQ—áÆ ÷¢‰#)2&Óóc<Í$äÙ%á¥Ñ}GÛ ÛAä¯ó¨^Nã[{ð…—£ÂùTÊK*êÎc8sæU¹ÍÑŒ´Òk¥ç‹#%‚dgB©3(zk±•”’ú™u(2‚¸©R +†AdÃ6ŒH–Ÿ/“½;Á]ÆýÍŠ%ùAû>Á·yÃÔ@ú‚j™fûž ÓÛèkÎþ±§•÷ÉPÑŸ[ú^ñ^#Íg¬_–=×Å'6s UF¤“ÃðìŽUTæÇ2KÙóNBGí}Uþ뱆aóf8” ß>s%µœW½–üûKCz4È Á"Š ­Ì]³—ëÕË—‰$¹Õøê”$±>ÌÅ£#)µ›ÄciiŒÄÑU5£ìg‚O#%\†ŸÝ0¹~/dëüsŽÐ4J:q‡YÛXòˆË`L§«wF}¤ësKáüo"‹`±o4¾gb·¦ xq=ë@}®ÍH%cæÉÛfõß|ëA.H4mLû¹Þ<»BêCôÕRn°Ê¾güUWÇÁ™JZAZ\¯[ÞšâOŠÞ­÷2 ƒIPöÖþѤA°^~Ūuæ¡éå¼ÿ}”ÖIy#¼Z;k“‚ÊáÄŒ ¤ºØ±ìlXˆKz`„P£h˜÷ôÐ#%(*HU°=s²«.Oa4Ýî/i0UhP·`‹A°Ìª ä÷õtôíÏ %O‰g¢]P¹g ·³Ö8 î8‡aDéCdC§:JÆ%z‰$F+å¼ó7~Ô˜CQ°;güÞš–.Š!hq,ÀhOiçO¨ˆÀ¨H¨”˜A´ÄTÍvœÊ:wuìA@Ú2=oYo.¢!BPÿÈ£åT¬ü‹ ›kR ík„àjHRI5_Íò»#%@Ýû²×6ý·ŒzÖ®[^M’ÛF‚9ˆƒKÛ$ŒâÙš»z5JX×ÁÉýE½¹B‰ jjšhêB"ü±åªµ$ìñÉî‹ÅS½ËÅ…=¥<5éÕ–V¼XÎPÚ<L€É;ÕKÙƾ%‹ž½/ŽÇ"e=¡eåe¯bjIžaˆU±´Q¼T¢ÈÇ© àΚ“Â)ùëõæu†òÈù»–™ëš¬—ƒ„gÄ›¦–î‡Ð>ø'•NçÊOw*4Íh¹j2T'+óÛ¸tN#)¨53lw®±($^ ̲™w÷ìmƒ]ðÓ MO&ƒÆ+¤!è|vU]ÏN~Þ¸Þ ×¯,AÈ| ¢iÂí ¥›\P„Ä»(#$O¶€˜ Î'gŸøý¿“0¬']O®PÚ¾ v H$‚–Š²HŠu©Ø÷£ÐöÃàü¢°nö‘QBA DŠB‡nÒõp]€>û¤z«æ>ÚódÄgQº’ÁCoW Ç 5=§Ñ‡Ö{S²\ùyÍÌ4 p ‰‰¸Q#Øsâ.;~;C§¬úÌÂǺo0íÔáâá_•B¼¾v’R¦9Ý«jÛçÐxim¦×ädÒ"¼Š:È9CÝAðIÝ"xBEËOÕ^î®=Œ&]œ”@õtQæÚrQpQÊÈ(ã…ís1QßxÐpÁcä žö%#$¤"$:”;~(#)H"„"vÄX¤b9ñ¨g[¼-±€žÍÎ:‰5B£«ýN#%!;RЬLcJB@s…Æ-kìãûß×ñfVXâ÷CùJcEíR6ö²µfv©#$Ää>Ý»<EJªäH#$Ðxƒ¾4Þ{ip'›¦Ü0y~“q†¦ËªÑÂ#%·ïmþkóSA¬ÌûÝ3:1CG3æCq™¢#%}QAÒ\ç#"éLfeæ*Éh÷E¤¸_g8[é]•¾dfD­#$zƒ!væoÈøËpij{Qˆ©ÅÌ0@ßÏž0zá½öÒQÑF­¨˜Òo. kÖÈ;±fTLI9–‰5DKœ°_«Xàˆ0‰2B|mþ?£mmCAË4{s 2? GÝùòð¿³Ê¢¨ª'×(³jlÂU9©#™ûrë˜îO~ýúoXʪqÁ‚Ã~ý`²=3¶[^èBöG/#)ÅÄùè¾wc´°ªªÖà ¾Ùr/Ý#%ê’;°¦/”` 0Öˆº{0:Q BC˜Y@q líO×D#~½k!ԶX¤ó¥&™Rgn9Æ°H¢I§NNêåk¹û÷j¤£XÆÚËe4¶lر-jQª›?&í–CsÊ”/¹…Åbaۗèù KU >¤ƒ*”ŠAd¿ðã1~ÿiw…&É™²DTÀi¢‚RÑ(–’$Å‚#%)°É±4) 6l›(HÌ-o¿}z­$#)%ä‚S”Ä¢r”·8ÁùVyppšR’u1 …NÆa³”ØpˆFÓˆÎ5‹›Ç< ²aÛKå,Ú§¾¬ë·Ø|å÷MåDf¹ÓJµ/¨N» CaÄL„ñ^ªˆ¦UY¨¥ÍK%ÚÖGî÷eï£i¦A?VîÒ€HºëÝÎó/}M³á“bãƒ^°ÂíØw³yíPèÈÀñÜ9«PÑÓZ+/JÄ#%ø˜'BÒNŽEZ¸Ð~sòêÒó*WÈN!'Ýüa!ø¢±1rTlÁ!­¨ñªÚþi&ÁõÚ¹Õ\pJƒRi¹#$Iüˆˆ0µ–ÁTÇ)O3›rêåom/%…¼&¦Á2Ìë{HFj „dÀ#Á¨Ð(1‘¨@£»)(Ûi%#%•ƒÈ*%+lË ›hÆ@Ð#$±€Ø,aX‘<4A#$ÌPÁ¤B¢TƒaSi,jþ¹+µF¯:íi•r®\È219%œ5ôæ€|›]Ù•tRè5J å¥˜™—HG #ƒžÌY˜6XÞE|š‰FŒä­c*ÿF½Þó].C<dâÆ. fšÀé<e@Ê*9“sGIö±a½Jw©£4¬*ÄqCE#q¸tSŠhid²a ÚÍMM …Ýh¨B‹!hIˆ#fG!fjƒ)EÕ,Eë•%#IƒpŠ°^ÆÞI £§®ÕlFah ƒL!ÙB´.¢œÑT†M¸äMD,e‘¨›PÓ‰§Œx 5ªØØÖ&á$Pi«Ç¡¹0„eQ2¹UiÒÇr&ñ`Ç#kDÛW Ö#)¹TNÀ #)ÉhÆ:Ú¬l¥[ o0™† 5!§Fâ‰AŠƒ¤;Yp+ÙÙ¹nñïc™ww´M]]fUtiÜh1Ðf#%M2ïmkK5£>cÆ1š7µvBܨæ®· ÝbË-­QÆV69YZ±¨Ô¼\}µ˜Ã’ ¿?ƒ\qÈÐcQƒí#*±.FA cklÛ È'{B°ddCÔ#%膚Ö#%‘׊Å#Ú‹SÊàÃRÍÓÙßZ³¸£…!ÆÜÁið£p¶AŠâä£#¸TqV0@;‡HÈÌå81`KV“Q¡‘ T[¡sD 2-™A¨…š‘¢˜s‰£«„35I@0Mqåø„ çd×BÍLFZx… ªÐ"Àl¡IH¤5’´‡"£Uëˆ CKÃC|ü|y¢Úzªæ¹S5eÝ¥ëRÙ#)ˆ#%T”% …(è–$œª&±§÷LA½Ýò1¼3"¯”€Î^†Ûxà莆 ±TÐA0T‚"m#%,-srÍG‰(›Ñìaœd›#%=®Ñ&ŒÕ"ìDÜņn,f½´Ìí7LØ0€Ñ¡#%’£X´4Ò¦®6żŒ±¸Öé; G¹=d{`jòTJùžÃ#%¬å.ù¾ÇXÞâXP®®b"UM}ú4ŽÝ#$cfѨŠ© ç(†ÑéƒT¶¨íñ]ŽÚäÖ1@‹uË4¨Ž%EÛäì€1æé­Ä`Ñ°EPH€‰Lèš" ‘¤´Š(ƒä€Ú@¡Ch*±%EQ‘R`ÀF#ˆ—´–ñIÄÁi£ëM@*Iûz{’5JS”6Ò¡Lþ4Åb¼×J¢M»Þ®¿Ùºoݧvܹß]öWêZJ#%µÛ-EŠF,HDJ€Õ+ª}ÃëíÊ€q^‚Ü ž—uÍœÀ**5AÓP Ýœ¸$Kx ºŸOïËÞôTAöaJa¥ÊŠ5ù¤ªÈ¸&áÝ$D!#$I‘ˆÅJ3#$Á.[\ñpù«5#)@ì=UîBsuÓëgi(&B‚)÷NJ’ŽUŽV¨Á PbüD^õ»Y!ô´®HÚN8±lmºººU·ÅqÒ‡wFÁ„í•Eªq¨bP¡›™ƒ`‚{מJ#W×Ý»}ln6§5¯ß+<H^´a¹®ì· Œ6oÊ#)h`¶gÚdÇUúþ-A¡[d=&m¨1‰vw’ÆÝoÆ¡âuÒ޳ݻ‹'t„YÙI€Û㺹›AIY£#Aù„‘ ý-vbŦ/’µ¹NÜð¾q€àÞÛur0O$Pš‡R—ΊŸ®èÌ‘&/7á—“„¹k”DÊ"tÀ»‹™%eí;w‰¢a9}ý;^ÌǶֲxÝ Ü…¯z(#%m6ÍôלåSáÕ¯X¬ø%Ð2׫ò#ôXv¢#%ô~sèub«frIÝÞ/}¬Ê6pŸÚÅ“ù }uó[~ “BGŠhW¢{Žòa#%&xQà`¡Äˆ‰!k»æN‘1rÍö7´ïp;-P¹ÆQ8ÂuµìÑ`)Òib¦½¥T*›˜;kMM ·Xè(#$ý?1`{ð•NÌ‹k#!:…²!Ús;Íbb"ÊQ`‘@U>æCèÉ0öÒ•J3ÛU3·©.\2'è~­/•3Jš·-*  ‰ÄC\ËJš4áÇA(a¢¦#$2¦ø5&º29@q5ÆtÒmú`E€„-“QUýôÀÓ+åMÈA˜ÒPcr#)66i=\£W®m¼Zé­õ«v&¹<ê«Æѹ«•76ºó­¬Qä«!¬j5¼›xÕãY5ãm£gwËr4k&ÛÕp$-0Áª˜@¦†Kp…æƒs”lZÖ#%dWÊ#){‚ên3( ¼=ž ó¡{#F4¡e$B#)‹œ ‡z‚ÕéSûpHv’0wömÝUÇîÈ<#%(8;å(üÑz=JQø(åö¢°ˆ  éþJIcà§J†è €UDÙùûÏ·šäµ»4µÜmºîávm:ª¡;{‹…wÒiuH#)w Ø M©",‚(Š-*¡H!–°¹`$PÈLKõ¿#%Ë}æ›m­ø¦Õ,Å’²B¨?»Àù [1Ð9Òü  ç#$M~ø‡›'Mº#)0µB°0È—Šb 碆&7•*y^Ö”\9Á%/#$S1{1f?iä ‡o‘±+­  èįH=§PÉ‹ÛG‚@'í1‹ "†ØÅ¥’2Ñ“F&•¤ÆÑbÊRRLÉh°Í«EµV5¶6Õ6›fU5™e«Ť¬Ø4ë}%£’ 0¬3 VÈ‚191‚•È‘>Ì)pÛj<¡”„dlŒ#ƒŽ""w ĨÖ`³yD€"­"pȱŽ˜W#)‰bM¡²¶2"ÄI2J‡P.T±A± ”ÌC2%0ˆ) #)Š€‚«Zê0B4_P‘j "@'Íù²µÅÚ[Â¥âÍÛvj]Ó³wuõ?UÅ$LAX¢HhcB¦ ´:ÀáuPÛ\EíÅMÌöâýcŸd‘;*}žÿO¦}U¥Ï-R¨¥{#$ùB#$¢A â•·íÉ”ÈMÅìƒäp‡x#)aLv|,:üo¦& LGŽ‰®ã­ÆEˆ¡z^•ÅX Á€Ð Ùú'|ÃQ¶WÂb>,£D#%<ô¤4 i5„[¦|¡ÖP= é‚ qÞ<òë¥uyeÙSfe ÷{j¹¨Ö±XÕj]Õ¯v¼Ö"^. c˜våK¤úáýDlK±î/FS¦\¼Û‰#$$ÐçÑ~ìaöº&Ҥݥô›é#$ö·ãhþù'ç¹hQn5ËôÚ¦Má+ésƒî銜Äâž› º©ñïåëçgC(Òe"¾Šâ ö賚Ӻo#)ø½J['í¼“Õu$qqÇKž|­Ð ƲíÔžŸ£´Á/Kxâ‰Äí<cn«¹ztDNÝp·OÖ̼’—#%M‡ós¯tå±|­Â8C—n¦åß™_ù9ƒ²;Qv@&ðÍ´­cw%ôxì:´×Ãáåž›¡êÊ]–Ý™å¤ôŠ@:X·2ŽûÛ~„e†j9ôÛ½[ئãßjÌ©ªfékG»óø`0œ8”èïª3ÛXõ̪Ԯa’ºðþJºá† ütÆBöZL4’8œnŽÛ"{ìöÈ»ï¿Gø{æû9âwlÒ6Dá㊠†d"xŸ'¶šém½]Ôw„æ¦{n÷%®†¡ 1ååRn#)Ÿ¦B_Šsƒ#%¾î┪ÙçÏèÌm-ü¦Z ¡¡‰ú{ø¾¿EŒt^,Q”HX¡TóZŠ•#%äAÖ(:Å’û©Nˆ‰ZîñŸèŸI¤¹Gׇ%w<´É?}‰ÒÅÂâa@µZ–¤{®¿Ô;ai­Ã´zIzö±Ì¨ë¥^_¬Œ¤¼3¸Ú"šZb¨ŠÝRÜiU³ö¹_ê}9nwÐi÷Úýþ= lªqÑh˜êCâz©9ªÈK8ñ i¶Ù{,UbFá3ËÚ¿#)1˜v-¯´pT¯!Î~×™ç8[¤"Ó=’ï~tÀá#%qQ½s%1Г¬>ÖpÁ¬Ú‡àÐÊa¨ÙvßÖ#%åÍg_* ø~b;M­®™œ 虘¶â­÷kÃÓ[é“áã§f´ÈË€çVè‡è·EA"dôð’I'wEœæy­Êª3o:,OíE‡@‹´o¦X—cÄrmLÅ1:c-ÛmÍé8â^Vm˜Ö)Þ¦Ä0`VŽ#Œ–áå5³…iÇüÄmŽÞ$rïbsLÚ§ÎñCh¼b9´§ŽÇ;o­n‡Ûû×]ÎОeä’yxÌÔoQ&ÚxS]±QãX‰éuðì8¤9dÍB8‚µ³Uîíä+4ñ{꧚öª¼æ{Æ‹ü¨äÑî0‰Çµn[›.ÜKc~H¿_'ÂÅ-{¼°^9ÌŒpäuzó|‡;þi{+*S¹ç/ÍÚG~Iñy§Íóóš³{É=/Þ’°p^WÛÅ—•zàœvx9ÜÆûW:ñ×c“Ó,ûfkOD»¿j<Þ‘(Ú#J#m»R­³žS‹ )u±lu;t#%_8Ùü*aª‡}¿­˜ÝJ<z&ƒ/„Áí7Ò¾÷”­?—WPÄïcé7^“ïéÛÓÌÉ߸uíÐ1¡4dEèÏ7Ç”qÙcPƒ¶å¬mÖœƒVp¡Ónåkw1O“ÒÞBm‚ÔRôœåÎ¥ ˜ß¡Mè,¦îlänÃÏy5SC#ŠPÐÞ´ø ×mÓ$a[̲Ržkô1½Ì-2ÆÕ'À‘‚9'Ñ—œ>Z‚š@#%´TŽ¨_1Åéª* #)J®œKÂÄÜfŽ™ìuLòW^!ŸÍw"㧖 H7…é è·òÄ™*†„&HmÆZëªê$æ”)¡ap×Q6“3‰•‡…Ú ÊËeØä\0È.#)tb¦qÉ##)Q¿¯×¨‰…Ô bÇ-H2€ 8á¹:>“öáëÄ-–4K³hò’Yr ™ŽA ­ù¦`ÇŠØ/lÄ­„†Ý$ Ü5R(ȺN¦ÁcxKÕANHD¸XD„Q0@H-†øhŽFMºxW^Þ¼qË;%·3™BԳá #)Î(çÀÃ-ÒIÏ!®£A25ÁÃ3Š€Æ‡ÕN;_/)¶#$9ÀùÄqA%XlÄÁÉá#%xQGÛUk挂k”P¨cX‰Hú&š(‡Q7è¯#)ˆ]4% I*PQõ n˜ªdæHĹ?!Q.×ïˆBí®0¶#)lq™S¾£Répû¡ÛÄúÍF6ž'ª!mÕú#*=Îm…Ì¢DGh´®ž!-É–ö’Åßnúª0â[?E´÷ƒà»£uk]ºcb˜ƒ#%±7¿`'¦Â0 AãjÙú›üôu[ÚW±9LÎGÑʾ:ÈJ‡¬!˜€²ˆ÷;­¶}ë 8ãÆòÑpŒŽe¸©ªÞBuNõÒ²TB<FPúèpKRÆmàJ01D‘šµ,yñ)l#)ËOãb¢q]ðø3<ïı(®áÙô¯VðÑ›Ûï.fÜÂN™¢¸­b0ÊV3õ¹iN® ÀùŒ‘ÔçMɾÝ6+³n9aéÇõ¾b¸ß¢ß¾%#%Ù²Lºã–鄺£6Í¥“X.ŽCkâ^Õí¾Aþ¼Þ=ãòç•ön¥˜-ß½¹å/ºí}ãW0ÁækŠò—KxÊ–¢aF<wŒÉ“²[y(ê§ÍÃsq yHï·Œ¶Ÿ=ÁÂx¦o¸fù‰/CwÆÎ3Ê™[V¨W<̦É{x09b%‰K/kKV%¦CîÆûi=TN*PUÒ)<Ñ,ûqÁS.•Ì¢É)/…@eÜ á8*jrbñ&°Tù‘æc!¢õ—tyÑ“Áß¹“ÍÎOÀí™-üY п6 Q‡l\a0…ëN6Ëð‰¨jœö£°NYqpÕ¢ð.XHSE‹8-²8‹ \½³Öëdyfgœ Ê9;ZØXm@ljae‚1™áCÞÕ²©«¨T45à£È¶Ÿ^3˜{©V“PØò•Ü5M(P˜3iA¡°€T‚;Ð8Ik#´âÆMûäÖï0ât#%GT9‚Hð˜mU’&÷ýÀ×]õûC–6¨a²l#%~ŸŸ(û}Ä-ðjÖ±úìÔ¼Þ‹ºÜ1ÓáÙ1ð;l›åøƒÊŽY’ ¿Á¯1.ªx<KÔntùw\—Ë<?‘‡œ<¿Ãû—W‡4;ýaë^ÂØÈö¥£HQž^’øCxCìC`ð8»ÍÁÇæ|ëkŠ)R,ó¹li݈ÕûY¿h¯m«»ÄÁY¼æ ìЭ#™¥KF™@¯#Á¥|kÅ×&¹8u]Û;­Þõy½¼m½m°lkåÉw[–‰/(¡–bH…›ÀP´¢¯|P*CRÛ[Ũ¶åª6Úæµ"#%±³¶ ÓEI96Œa0 ¨2*"#)¢Œ…JrAîñ¾rñ¯ ã¿b¾Û#)(PC€èôgwcTvºÕýÎ3Ì,˜Í÷o¤D¿bq^¯xÛ‘pƒ)T<¢$‚‰  U#$2 2˜JŽhnßsT˽>Z¹Vð}ðÍ}·XñËe£ ¥¿Ëâ*Æ1ƒF(©QL¡ÎùáF–†àA¦í¥ VS‡þ?Ó0lál‘^#)1¦™@ûæ•\“v#/SN0Á*Ì<òº¶Ô#)RÎ|óM±A¤+ ÊB4È¢Äpùq§©àË#%V®ÈˆÄìÊò”Ä5·€òJã0‚j4›M¥Z’)a.UXؘÆ4C)[ÌPåf²Fl²‹XT±‡tÊ]$…Y/O¬¶áí6 ŽBîMÕˆ`&j4àÄYeݸÅ5en°ÔMÀ(Çc^Ç{8cZhëŽv²ÍîFjh£MÔU¬zkZÖæSÇ‘ÂåËÉÆ.3VÊß ˜ƒDE¨ÝŠiU €å ªÅO.QÖ\16Ô³‚"¶&s-~›ÞµÁoDŒbE6ÈvÎS¼ÊΘ´Ø&0‚9Ü6(JR#M#˜t³LÍa¦uŒˆQ¬aNÖ•§¢C›#%,ªoW2¾¥dÉJÌ$»ÖÇŒŽ8Lº”UÁƒcrz¤¼}âµÍ#)D‚nKÝÛWm¶ñwfÐ#PŒ{Se"r<”+C׉ÃVñCDÒ41œ¡’±ƒjÚ7 ¥PT¡V·š#%&#%¦#%ŒM0¾5hÛÖâÛÉ’Ë+·®v,ÚØM˧Ƃ „Øh2‹…³wdeànkh0ÕŠócºlÐB²d)3É“.ßë9/ ZËß`xïh\(4Œ)±å°~Fœ‘ùÉ{*7ÚW¾î‘05½m±³¼Ã¦cØRDØÛÞX5X20q·ºn …°¥#)Óa]`wF:ьд´yÆpa;ë+`æ¥5J©XùyN{b¢ã™Ã&FÔ¡áYUVÒ5‚‚§ûl(ñj>31öeIÑðV"Æ…¤Zñ¢×ÙM#)/ i Çn  çaÌs)Àñ¤™ÍmÖ‡Ã[zX[2”ÀÓ&¤0ËÇ’m£EÀ}ØV®n`L3ÉÜ$g>»YŒŽ§â4ñ¥¶Å³<e`æecUyLjTËlÉbFÐÐĈÅ=Ó9HÒ TŽ,„ Î ß}f&º³ Þ °g~ 8¾d΋Yã/z»4vø¯[ØãîõËŸ)TÔF‚‘)GaeÝÆ M¸ñ1Ÿ0áîÿ1™^h0XB4O!}+J‡¡¤¨!$/‘¤(w"õNùP€}Eàœ‹Ñ¶Ïd£^É^Œ2(ÓUÚA܉Q‘ÁWžßB…ö°¯‘¦Ü'éqº²±eÓ®38™~§WÔð~ð’@ßõz5ô @N-²I›ü|ý?FÃëgJÈ ì~¯/^zx>zH10öœœÙ¸›ËŒp~¯/ÜKütuŠ#%–ªW—)™óñ›ÜõÛá"4ÝÒww&#¥ÇvèÈʪsÈ3<Ø‹g“ÝÆuêNÐ÷">ð:ž1pa8¡I¢«7¶Šjä~¬B¤ïéêMMÏ8;ú>Êš–Š€'qU_~¦¥z71µ²©”ÕhXªˆ’…´EK-H/@ïýSðQæD6ιíî«Îi$ BB6©QSIm¬(,†#%ASI#%TÊÙ¦McY5«J6Ä›IŒ1›*,i¦Í4¨Y%¥’¢(bšƒQ)Fš)š6M†•L- k 6aB‘@d$EOÚ_¨ó•å0TNX³‰Õ´LEõÄ·oDìg-ÇÛ7  ›.f'·p ò.†¿« ¿ÀC#$M7‰Óèí¶ÎFïMŠIð—VÐO0Ì(,‡…èY$C*‰î –ÊAça@mI¤wö›ð†Œ]û8ç#%0ÒMM«sàã:0þ¬xÂÆI#$Q’#µöm‰o7•ÐsÛԥȨ˜’Ä.!ºòI!uFšÞþcÝçñÌ}Úó7‡>ÈÀ½U âY®¢FøèÕŠZìs S¨`MçôÕ~µ~£m£%lšÄE¡”F©5¢Àµk›H¶Rj1X3ñÝ«™¶Ê¥å@)T¥,…”sà Û†•ˆHÂ¥ !GßìÌ.#%eYl}Ö^ :{ À Ï›h…Dv‹Xiâ0Üü™tj±„>—gCµEh¦¤#”˜ˆ¡¶ðrA¤E ºr’³[0 C,[ ±w$Ã!l$X¾C2ØÁTF£CçÎ×™F2”íïšù•îM…¦`1¬iG~¸³5 £31„JÀïy”¨XÄÄBŽ ™‚#훞4qÓM'øÐj%3äá ÉŠC’ ¹rÌiA$yàˆ`Ìëæ!r6ÞÞ¶» ™‰\µÄ›ÞÚú_¾'÷Ó•`=Ÿ®lG:óxÛÍÈÅ*‘ÑÆE‡}&™ XCµÌ¢L“¸ @ç0H ¡#%ÌÕRÏ[.¨T”KšB€Ä™ @ГBý¡‚ª³ † êßѺL#$Ãgšb¥*‘$@ èël'“öái¢¶oÙë›Ïwo>œgêJ‰å(lÏ Ðõ‘! ýØ_ÔýX“¤ª°lyy•ƒ.j!ï:$ªŒ¶\°\j¦Û À}ŠÂRЗ0A†}µ#$»¡ÛÜp÷ªüfýú•‘¶ #%ÀÛ:-«Ó·ŸÛ5j»j2ZúÍkï÷ü3M±±ßwƒD³žÁûÏ…Œšü#)ƒ­ùF“© iaáñm1Ø-ƒ¸Ë5üL?mJ?² $>%JL! Òjw‚g7E$,Q“bîŠÛ%Ö¼åiÈ1ÒЋêôž»‘:|C‡¦.Ä6WÐœ“•®J4óêFEªèÀÆm{I ˆ2/yÏRî;¸ºÜ»$Ìô|¤Ü»b&pù̶ù)s@ïJHRr=F½Iþ‰›ó0l8Ü`ð,ív»Ëêâ„“6z9ˆ¥ŒLvÕ‰²ì Ø%Î$$(’¾ÿStÒfê¡‘00}»~„n„†Œ=;O·èw°ˆEAI"" ª—Ÿ¬z¶©Gq1/±ûu»cETÄð¡ÛÉÂHr ™ö¯¬VÌ~WxÍ“ó«¥tN;ÕK!UÓE†1‹1ãvTÎïÉ z!ºV˜<L˜ŽEa§´õ†ôíª‡inÀ;½‚ ™_/­ÛìÃx`¡ŠÒð†tÊðÖç¾°çkxÛš9/Ælèã:ó$g,!`Øé©=f)1é¼ÎŽR¡$i©ü]ñ{c…H5áä\]ÂÃ>,àD¥"O3OÃÆ3YÎú›í”#%[0Ñ#$po4DhÀ¤FÀ;â3"`âPHšŸÊnªÄ°dþ!QPÓSœ‰0|ðÎTS]æôofø#%d3œ¤Ä`$™PƒIÌV\€!8Ã%Œ7iŠí(£Ja-4ØγB#$caé 8 œ@6MSg~5,3g«ac\d¡ƒ.— 5ß»y¡‚ð¹–uÇÌ°·#%“zéery06CÅ°N,%D‘#%h¨Œr¦§™ü  dbišcBã‚6 Ñ‘vEyH÷‹#%àh͉*·ÛF'\kÛÄMª„À A$‡ ƒ ,N‰ÝÜ`ãŠR#")7BuÏ}zèãp;몕¡ˆM“', $jÚf©Ø„$3j °/P`Ê5Qçåþ¦qÆë`}ºYÉ¿ŒŠ‡v‚L!Sœ¢Y ©ºãhj„SºÜ‡"(3Š•æ¶Å~ù„ákŽd3Ç7‰®—†à]`™[‚KÏ}mÙQšÃä¯M»‡NJ`–t[½C‚í:J(7f²”ºÆyª/¹˜S#nìżÌ3Θ”J¥¯4½i Z €c#$ ½-`•‹hl@šh8ï#$ZbÙ×5s#){ÑRã¤#Ê*I·‡™òŠR®,,gÅÉ57wyÏ›¬ÁŒ«ËH<fUß„¢GšÁi‘½ÔË® —fZÄ3åãû"‹–^9Óᇉ$YXß*+ ¢æS4 „×i#%H#$ó!œäg¬$eìÊ3 ìЫåh0(‚k^ ¬â#«ÊE:§"§u×n؃H¤Àì'~tö\bIIeñ2dú:ÇM¬ØÖ–DùG$»h·b¸EC`LBzáƃ-#§#ìƒ:²¤ÛnE-–Eî9€€Ý%Âj„åH«j.·Ä(Ö¦EED&µ+b%7ôjH¶BE÷wʼnžŽ3ŽÛ‘;T_W…+ý¢©mz¦Q u$À“RK Ô#%p%&™2;z%³1a.¼‰X>uYÃÄxW5­¨´MxƒwÙ…›jnªs¼7 ,SìÕ$k¼1úÎi¡ð–-uy繜gXf€‰–ç®u I ÉR£‹ }©¯§›»\Š~EªÃ“,ÏRæíLL1ÙK9ë×G®N#%tšLšŠÄS(ÔnÌHÛ&l‡í¹Žx5-ò陉Râæu³‚À8âÆUÓ7K¬áåß‘ :ì2k½¸)òJPàpêûè,Á™’Ôåš›“HaÒ ƒ®†a‚Šca²ÓM‰#¦©b,(B&’cC¼)ÃSŸº--Î(2Á^ѺÓéi"è®’ 9s¾#);ʲUö’Ã<] sH\­Ê9ÀcÚâ1®®È’“‘Û¿à·Û,iÍ>ã5¿K¢“P½G#$¦*€j®5ožÅ±äÙ %ÓïÂì-ñ†!DºZ:ÙÁntQÓ6‘Â$üoöùJ!í²æ\6E.@ZòXšh?¯'+o;o­0Œ€„#$ÀsdìM^ðk¿>%tÍ« nž8ùùÖ¸cDsM|y’®/O‡!¨ï¦QR§Ðai¬½žm£y¿!`L°º²RkPC6 JbÍ;ËIñÓq!-oŒÁ¬,¥C´:lSCòÌ/ÓÎèÀ娘ˆÐ@m‹˜*ì!ÀÌP•:OGë´´¾±˜ñ‡(It ºpw¹—ÌïÒ]»;uV­á#¤ïȘ}^Üq“#$…³Z«&7€pŒ˜Y#)Œ%JahüŒègÐjòç"¾]•ÒÍéSfÍÑÍ"­¯«Y€kyLôgÈ‹ÎIdx¥:Îé𖦥59ç#†b¾%ÔM#%ïÁ@áƒ='<ᆎ:¢Æ†0ž9¼1âgÓ²cM÷s1Äo¾PÍÁk±4 $´òˆöbîó\U¿ Ä,Žt˜öÃ"Ù¡n${ ÎŽÄHÅ S%…p@¸fƒ8¦òCi¡›üc„6ä„E"GņdŽ o¨a¤‚+Š¥#%“Y5;¤ÛÝyàët÷²Ñ9%1«͘(Á7a†Ù¥»(dÀ`¹˜Ý‹DÌ" #µƒ)Ê3+‚Y,#%#%K‘° ¶‘q\] STŠŠÆ&ê`d”^Ë%3¸h³Ëã|0…Õ/e—o™Ô«ÊÓ1¥±„lˆbßV•\u9º£(в Ú¢ ÙÕ‡È͉£”Dž¤H14Þˆ#HaØßÍFFHH¨§#@ÈÀ#$Èv%‚\¸ê˜˜šÖŠ¨ÕѨ›3€Þà¡P›•#)Œ#%Ìkb‘À#)eç+„3œq»§XÉHg8i¡8ÂB¢ê™*L"ZBÃÀ8¢ ²}IåçÒ|L{(¨4´TDP;qšîEÑ6ååÓ'¤laíä@‡Xhˆ°‰ 5#$¨Aû÷ÿ„·–ËØ>G¡ãìz›Ü=Ë6Rj%×ï+LÔßi©Júg/Ð-#%D±¼’9tæEî /ÄÆôÏe1mÈîºVáÃa7K¼f$ÐÇHÚÝa€Cj —¹$eÈbŽ&ß™,W˳šë–µyUAéµ º29K–Šë¡A7ÐgꫧLJ”QL‚lªG„Màö3]¬ÑðÛ½4ÉÀx4‹Š”µ#´¹DMøÜ™b†‚dçVŠ­Ô>û,’û ­d}åh/¤‰¦Lžî¹wl~ \ÐóìøÏ&~Ö´®¤aÀCŽVŽsJ.€ZõƒˆòX›/®ª­ ¯fãs½5‡Öõ©Õîó7PÞ­ÝÐpÆ#$H_z_|þö¾à#$Hˆúèljï>Jt0Ý"c\ŽWÓd{öÅT°“ëŠm²›Œ\z~x¯ Š¸Bg/­;µÔ^W¥'n‰Ý.Ûi‡oyá* ㉼ƕNÒYƒRÁ½ò9wmy¤„’Q¯$Ôï$ªDØÀay`˜Øì! d…(…K–@„@¨¶XÕš1Q¡Á Q™÷Rd+#$ñN×ë$ί=Üs}õ±Ì&²š24°L0ÑÈ.RIðœ 0ß<ÝG_cëíð ì˜÷ÚkµCæQ:¢ À8„Æ'ta“kÊKUdFwV‹S¤‰JF*Bw332ÌAQXDƒ@å0¯> õ†ÐÊ )ú†Ž#)§¿ÔJ#%å çæ`.‚›ØÃWïk9õ}–sì4£ª£JzÑ›*¼ó!d?qAaŸ/y4(ˆPø“„Úð¨ÆmëNzkÒDårFNþë/X/ËF£UɸÀ¤Ë–q™oµ5?TÌ›rŒ›¹aâ ¦0†° §¶=#)‰~nx/G«p¹&§#)3pfédø]¬ ÙÆ%7r½€?ıQˆ¬Dô ¤ø›Mm›d5ÐÆŒ 1­‚‘è_”¦DõÅ*"{` E%²ÕiFj¢Köed¶£jMQ­o‰­“m^ûn @¹Ò¢€ÞØYV¢H)dˆ7HŽ P"@X M€¯d@v€"ûokqÜD…¯÷h#$°#$c&þì?L›ð5øþ¯o»}­bÒÍ&ŒcXщ¥ïßÝÞLÕXêg 2#$ýÁ‚€Hˆ*$Š¯Ç#%»}¥÷÷Qã`¬óë7!݆=>òt“ÜxÃÓ߸[1F1œ‹à1§˜ 4a¢L"CM£2XVK’ƒaD"ŒTv¢DÈÛƒœƒ#$Š´ˆÐÚPrP¢BfRa ±#)‚í£ $â'Q¿K£Ž3#$4*:éŠ#)»€,-(l¹‰f‘²½}j ØzcØöŽ/4°À„Â|×'Oà}Î,5¶XÐÔæCNï…£gʦ%ccU ºÃ¯¥°v€l )rÄ‚Áˆ#$¡±×‹#%ÄEàb 7ü“´D“jÉRw–¡@$&T• Œa„¢ïih/|žœ:C ™g†¹Ee#$Pf¤¼r!Ex{2›è$Çml”ëÔöoÆÅ!#%ø™>ÄÍÅ÷è²mç·#ž¦ßw!‘+Ž]¶­6Ó¯z†®&â¤;%¼6WËvrr?&\Êᶈl‡ÔS«¾:eOQôzÁöE#%†±A=…†*A ÓMZ•A†[á#%u%ïé!rKïl|²êO¤ò;Wݹ#È çÂÇ¥e­ÊË5$©¦¢cAm[0ØJ„Q³ZQ­R[÷”V®3•Ò@„R‡s¼ßlñj_Ùˆvè®ÔÜwtM½ó7@<îˆqðÜùˆ.K­Ý³Ž#%`!¬E&|ˆÙ=‰Ýà:õSwPˆu¢w©ì}·#$ä,Cƒ³£RIƒŸÓáÓ²('QïÀ}ulÿ  =$4KŽHÇ×,ŸÓÕ‡|ü☣qÇH¤A¸ó½¶’a©»¤îâƒ$ã,%»É`ùÑe®,§DL\³–ø…héñzI¡Ô\#hÚi–Røj'»uM°t‰¨gCi¤iKb Hm‚bÇkÍ<=\”’e>³§|%Ã;í[T†—°€(,´±%Ô bÊ„Ì̲ÑùµvùDMçrœ"1ø} Ì‹fȃä8Vqª:6bßæSXˆ.^Íínµ‡'üKêŸÊÍá$‹¦GNËôÜ´&$C«¯ÎóÉa¸®õݽ@P)eZ·ÉA.¨ú#$B§.ο”ãa.}FÄÈ4“«#%1艂dA¼O0@@#$UR";^¨ôñÁ7ûÏmï»àP#$Wq×âw†(#b wIT›N‘J0½aEF†½g°“£Å‚BBq;L°×}ÏžÛü½,Vì£.ÌgÚ3¤t(p›«‘²QHV„Ö½îa÷l#%Ýîùiäµd#)ÚˆXy½C^Á¯ˆ§`«7eŸÐ‹ÒMtéô÷6ê¸ví¢åU¨v‰Hczì—n^6÷ÙCGK¹Žkä[í*¡à#%Æ…>16‘rbJ;ÕþØM‚í†'_SVkËïQF™1™<W*+$»µØÉe}ÝæA¶+÷g=òóËÞãiÃT¢›!hÅ•äÝd#)ò6Lj6¢°†1–êM©–Öí¿™JFÄü ØD6ŸîËvߟÜxxÊ((õî‰â`|,ÏPxVšALGÎkó_Hõ*tõ‡_XH²+"1F2 Q‰'¸èO0 hxÅ £ €ŒDeŠ™Z,–Y’Ù³jüÿ³ðj¿‹¾ñ¤Q¢ÙQ¶4›JSMªü»oåîò„Ëæ½cè"¨TDÏë„¥­¾Á™—BSÌA,HsHNt"N£ò`¤ÔL#))TS")±Š‰µ–ÑE¨£ilÌ[4iïžyª“V–›RŠ']ˆ„]àä€a~P×þOG-@õ''Γ€$íDM ‚sÔQ¨#}*ið‰SK²ײá 1>A‹{by"À‡`i€p@Êr2Êaˆ’éÅ=ØJ*½Çw‹!I ªNm'ШW®>Y˜’ú)IFw B$„²~7'-=6#)Šî€Z³#%^`:£ód£ê²&"‡4öP A}Š@*Rf6›#)XZ“k›¤µSmF”š¥´ •A‹fÁRæ­¶m&#$ª¦ÎA]”+óê`àJ”ÏH€H°õRR÷jc×Øméªêù$Ruc»Q”ó—0¹{=9¥†ÚcÐòø¨[:gÒ‘°ü”+%3OŠK⤌‰C¬­˜¶Ò”qÎ.9˜ ~ü;»šãùêCf¤<ö6¡ö›écÚ>åóþðÊ#$”d4g…HÓ’#FLJ’4FXÑE%Dkc&ªÚ7ï+~E¯*mA´•¡Sò~RJ T U#)#ð‚ŒB~`é“v @ª‘ A‰‹#)<¨¦‰Ÿ1­ˆX)vêç{çÑë·d´×sˆõ"ÛUæ´[QŠÐZ”* µ6X‹hÕ©¥¬©~©µòôæmògnöÒ«,”4åëÇíTûP¦S?Ë3…ö1š‚ÔA#$e ÆoÎœ’Mþʘdú§!» Ei).賋kŒ¶!´F8•$ TgyŸÝƒ°¢‰&ðBÒÈ]@¡–Ñ>‰+Mƒi¢•5îl5ÊZ7mwvWy¼­uM¢É­ê®J‹y»¬ÍÙ™U×7mnuC5‘e«Ë»¢mwuwv´›*L˜ØÁ¤ÚCEÂD•8)V@¥DŒTm¶Q¤#%6âÄ[Jilš“,«•®­åÕמv¶ñ¨¶Q™•e²–¼›uwn²Æj˦ºMÍN¯‹œ4<Òb2B¶Ä˜ŠÄEË‚ý#Ÿ &¡Ì’pÐäM–%ü íý¦‚´›L¾ug´©­]ù>o5û|g3 tR¡úa1T,½ø˜1©›$™:¡@ÀœÉÇ­[Lànw¡™Ý®K†b.¿ã8eûp#$0}0’ì$œˆöwêž|¶;ÑXE!]7»“y<´œXÛ8ÓÃ;ñÑí^îã~07"ß_ úAi‘‰1êDÐÕ)cS{é[§#)š`SS9¹„c }^FfÌ…:³ë·œA¢Ž+”R3‘x\ëÔ˜Þ9„þkÈÁ%Áæ1‰¢Zmaãéç/ZÙmÝù줗õ‘Ñ™o'ý"Ö±ÐéõIZGJƒ×[ë õCHbyq• -…-Q”4ÅHÇ¿å&{$Õàòí¡|šAïŠÊÒ#Zi^ªŸë7#%R„¾ÍûN¡¾;Ž¢,é‘|þë{öLͤ$~zÄE²=z’IhÉ‹’…#YÂÂ2“p錦-*P`šl.:3m30À5q=iÔ& ›£°5Ñ—@(Bû“'XYŸ7käN% ÝCd¸²&)vCÞ`‘l±(´ãWVW¤Ý=à?ËÃó@°7­Õ ñDĸpZ¦ðËò’ 6Q~ˆ§„ ÛsCãÁ–ȃ·º’ËŽxFêßë¡jIb8j«Ûäe‘R–é^Ó³2#%ƒúdf•,?Š*Ûr½Dæv+åV¶ómA¸(¡“ü4,ŵ9•Ò‚k̃#t[Z…]»Ó‡G~ªÜØáÂ\#)r£}÷C“{QÑ„îr§½{í´X q8p–hæx%þÄÌç†b– ¯-.9¨.Ã,¸ó£Ìa‡rM¶ Ó‹~BœN“xó$ÉôKšžg·é± ‡I¨ñ>iÄ*ü/³Ùl»Ì´4õ]1~Ü „O¿/~+pAÎyR‚¸@±HnG«N9hBÆÚø¤v‡„òG¡è…ÏĺïݶÙ>U­Lä¸+“ 粫hÿËÿûûêÿ/ýýÿù7ÿ?ú~œ?áÿG£þþðÿ‡ýëÿ“Uïÿýÿgú§ðÿ¿þߟþ?ò×þÿû¿Óðúú?özôø¯û´ÿúû¿ã§þï÷ÿŸòÿ…ñÿ‡ðÃÿçú|Ÿé»þ?/ý¿éóôÿëåÿOÛöqÔ¨‚߽Ʊhü@€u@íl€èº•pÿ)ýÐ@ÞM„Áý>ö T#)¢&B€sUs0)*ÿäŸß¥S»±$&f}ýo®Ùm~·tJ#–{rôk¼ »´Ú!®<ÎHTÕ5„ØàEÿ轤ëïß¿je´Ì.l‡ö‰noÝn*j˜=ÑÖµFIÊ:ì+¾H1 ‡0Z¾Wf±‹:× @¡C×ýªÏ#%o™ä§üþ–Lõ3øKþn6^ƒb9u(ðÔ˜ÌiÇú¤Þ* L„‚ÓI }}Xך«Ã”ä’Þøïíü5ÝòÎ$|e­1Ìv±•‘ž#)Ù™þÇ``ÒUÆUTÚ”Ï#%&QFišÈU4Ó.I\Ã#%ŸúphÔ!2ªx´o[R¯­4‰—€ì`G… “³ºÖîN&ËŠ¤ÉÊX!l– ÷4óCMÚŠ;V©³n¬fõ6ÕÆ´8˜Ì‹Xqvak#%í] I˜qx“ý2Í…’îHÁ#a#%pÛÖ=_b°-9¼R6wryÑ$´¡Ý#utFò÷D3ÞiÔ=Ðg=3YGýÍ„e/€ýß 6P®.f,‚Z"fVHÄb×/;bò#$c.Ð6‡í|mÄÎTcæQZ§ðHuo ‘ØáÒ2jǵ.cb¶*t\¾Üº·ýjÄzÿÕ@ÐeAeÉãÏgÍ 5æÞj¦+¹%Y‚sEŠD|i'‰aêõá.áI9C\¯0ÛÍïÜømà kÁ‚ª‹IRŠ]üºoè?hz˜ñÐ,g35V#) yâˆ(RcŸ»n)Ù·S.‰¬$d]®épÅC(ŽÅ€#)ÈÈ ;Α¢üìØÂH b¡$cÖ2âï(wä¸Ëb[ØAöýsÄÌá‹ý’uÊÉ€Ÿ8Ü—ÊÎãb‹&#$œµ¥#~F¥»9Ö‹Î0•ssIVÒ‰#PÞ.%D¡¨*‹VR™[bÔ–¥4ɉ5öݵvÔ¹eH'D@Àˆ'ng¸ö‘ âë"î‹1áTÝo0PE-‹'CØPq)¤¬Qg?ˆ|C˧”±þùgù­ãUh„ßc÷CCþÍêc“Y¯=˜ŒPø W>Ï8Ï/û|6iš·ÆZÈo´ÉÛRÆpÿö_ÙCm¿¤f{عe|1È·Ô{:«SfŠï­Io‘-u#$8cÒ"ñMòiéOuƒö~/·Ò&‡“#9Éf†P4Ò©£Ë3|½’l{ŽÂ}4Xj!>z˵Íãt¬m–è¼e¯«Mëò6[A”¥4­Ùap†„±o£[ìnÍ~½Z@­Ísi6£mËSJŒo·ª6øV·¹ڤіZ mó•\Ä™d,Öì ÍZ#)^ðJcvPääR$8ªbcþ*S÷—ÝÂá$$Fàn1t#)"u:»l\í6)… bSß·i%ôì™BkU¬ŒŒôRѸ(;ˆžû§Õn¸‹˜œ(4’,€HÍãà)_ö^Ãçî£Í“ÐŽÔs}\EðâQ‚ú˜póÃUþ¨‚À ¡µÚŽã³@h’”* @W#)ݶÝe«îh›5&½¾ÍV<¡ H$€ #Šöú½žìœÿ_@‰Ø„xtù@òþw®c#Ûmh‡bÝÑ::1O(4AŸ“Ãêûƒðÿx…Oùõû í&Æw>(YÿÃÄ”"×ýÓn–ŸÇþ©ÿnþ‚©ç10…?û5ÿõ´¯M»Íy…]¡,Yû;ŸÓÿËû6^U÷xzþ»6ì’|×í#‰Í>xð{Ç]Üó#¿ÁÃÚÈ@{òÂÏ?†ñ´‘ì×þº‡Ÿ¿€ÑÿP‰ÙóªyˆqLbøá¥áñ—ŠåÊm‹#ñìÅ×þÕf2ô#G3عBºi³GÿŸPŽ¿2¨•˜ŸéœÖ2’ïÙGí©Gþz-g¥vñ8ª"™Áתáq–s¦ò8‡ÇÙ´éÛD è2²±û¹ËxFá‹Ëa,oÃÂ4N*%¯+iÌ$]ÏNyfαb‰Çz«ø½n»2X¥ë&¸ ²¼>ßefcÓžÆÞGi={í>Ÿø«#À§œgPÛî1ë–«ÂO@uk§øõÿÄc±üÐdcù?_̈ÿñw$S… 㥧Ð
-#<==
-#-----BEGIN PGP SIGNATURE-----\n\niQIcBAABCgAGBQJXucfbAAoJEEm0xnwFJ3qq9HsP/jfAbxzLd6uHMfpZP+GgGJeY\nDx/t+p8lKI3r7VDXLB4UkzljBisNG7/uNhHZ7FB86JVP9WTKOjkRk0a5GyjBHI2K\nNFBxC/cUljiRs+2NA72MPFUkGJWG7LFeYihWhpto5iNDGtyBGxYOn9+wlStixOIP\nVVbRkTxc2Pda+Rwqj3zfPVZHXWxv1kPWhrLth0Y/20Q8CBVa9QyDK6VoXgBY9Kr7\nOhijnsGAX2R+CVfR4iCM5/QpmJClWb6+ER1bPrvyCdTsCzOZZLzvw3+U16YYzrGK\nWj+PE+Lfbj7GizItZSB861BHcl9yGWd0p7MxYoN8tKZljTKksF53r/QXm2JVkUCZ\nl6vY42scP3tThgyBU4OImokEfw5GY1JbaEtzi2sB7bdzcMVgTCWrzozJYqEXXGlm\nPY9Xsqr8p0YZT6T3l4vFAAxoNZ7vBcyxixiOzkygX789/vHurylRBLsH8uSlMt9O\nqRPEV+c/NzzfrxKPlfjvX7Jzzh6Fdd8QzLheKE4skSwbQ9Tb503wral+tPSbKvGp\ncU9PWFlB7W+TiPua+XPg4wkA06lwo0yhDH5wgLme+LkeUaQjHo6+MCweYjZtSPai\n/fAre5LUhHrUpWPogvWSSdq6eK3m76utSSkHCJAj8mvhTWV94A21Rv6dutRvujKo\nCQGdw9yT22XvPzQZ74b4\n=lYLz\n-----END PGP SIGNATURE-----\n
diff --git a/linux_dpdk/waf-1.9.5 b/linux_dpdk/waf-1.9.5
new file mode 100755
index 00000000..514ac755
--- /dev/null
+++ b/linux_dpdk/waf-1.9.5
@@ -0,0 +1,169 @@
+#!/usr/bin/env python
+# encoding: ISO8859-1
+# Thomas Nagy, 2005-2016
+#
+"""
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import os, sys, inspect
+
+VERSION="1.9.5"
+REVISION="0c9b76c74624eda0738007bf6caac806"
+GIT="57ee30760a44c474c292bb67977b9a5d62bae58d"
+INSTALL=''
+C1='#0'
+C2='#/'
+C3='#,'
+cwd = os.getcwd()
+join = os.path.join
+
+
+WAF='waf'
+def b(x):
+ return x
+if sys.hexversion>0x300000f:
+ WAF='waf3'
+ def b(x):
+ return x.encode()
+
+def err(m):
+ print(('\033[91mError: %s\033[0m' % m))
+ sys.exit(1)
+
+def unpack_wafdir(dir, src):
+ f = open(src,'rb')
+ c = 'corrupt archive (%d)'
+ while 1:
+ line = f.readline()
+ if not line: err('run waf-light from a folder containing waflib')
+ if line == b('#==>\n'):
+ txt = f.readline()
+ if not txt: err(c % 1)
+ if f.readline() != b('#<==\n'): err(c % 2)
+ break
+ if not txt: err(c % 3)
+ txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
+
+ import shutil, tarfile
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ try:
+ for x in ('Tools', 'extras'):
+ os.makedirs(join(dir, 'waflib', x))
+ except OSError:
+ err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
+
+ os.chdir(dir)
+ tmp = 't.bz2'
+ t = open(tmp,'wb')
+ try: t.write(txt)
+ finally: t.close()
+
+ try:
+ t = tarfile.open(tmp)
+ except:
+ try:
+ os.system('bunzip2 t.bz2')
+ t = tarfile.open('t')
+ tmp = 't'
+ except:
+ os.chdir(cwd)
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ err("Waf cannot be unpacked, check that bzip2 support is present")
+
+ try:
+ for x in t: t.extract(x)
+ finally:
+ t.close()
+
+ for x in ('Tools', 'extras'):
+ os.chmod(join('waflib',x), 493)
+
+ if sys.hexversion<0x300000f:
+ sys.path = [join(dir, 'waflib')] + sys.path
+ import fixpy2
+ fixpy2.fixdir(dir)
+
+ os.remove(tmp)
+ os.chdir(cwd)
+
+ try: dir = unicode(dir, 'mbcs')
+ except: pass
+ try:
+ from ctypes import windll
+ windll.kernel32.SetFileAttributesW(dir, 2)
+ except:
+ pass
+
+def test(dir):
+ try:
+ os.stat(join(dir, 'waflib'))
+ return os.path.abspath(dir)
+ except OSError:
+ pass
+
+def find_lib():
+ src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
+ base, name = os.path.split(src)
+
+ #devs use $WAFDIR
+ w=test(os.environ.get('WAFDIR', ''))
+ if w: return w
+
+ #waf-light
+ if name.endswith('waf-light'):
+ w = test(base)
+ if w: return w
+ err('waf-light requires waflib -> export WAFDIR=/folder')
+
+ dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
+ for i in (INSTALL,'/usr','/usr/local','/opt'):
+ w = test(i + '/lib/' + dirname)
+ if w: return w
+
+ #waf-local
+ dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
+ w = test(dir)
+ if w: return w
+
+ #unpack
+ unpack_wafdir(dir, src)
+ return dir
+
+wafdir = find_lib()
+sys.path.insert(0, wafdir)
+
+if __name__ == '__main__':
+
+ from waflib import Scripting
+ Scripting.waf_entry_point(cwd, VERSION, wafdir)
+
+#==>
+#BZh91AY&SYqpF3’ÿÿÿ°#,Êÿÿÿÿÿÿÿÿÿÿÿ$‰Má ð#,E„(aüü÷=n„#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,>û7Ö>ÍÔÌë¡;lÖ†#,wuÉwÎîÚ6±õ˾ï^)OjV›î˾›—·wÙΗ;vØ.dù½õ}÷·Û½ºëÈK·R×DîÜòÞc6v5ëB‡­íîÎwݯ}šv·Ó¥ß:Þs!­›šÞú}õî‡{ÖÒ«½™Í½îû··†ƒëÞÝñ¶‰}÷ßwƒÞÇ3zeß}®#,#,#,Ð#/#,|ØÐÇ¥#,'Ý€x³eÌ:ö÷l½ÝÞǪr÷]®6ƒM#,Pu×;>¶N•4#,#,Ð4i­u¡Jíš#0˜B‚¡)RvÐWc[H¡BI#,%QB…#/ìA½ô¼÷½Ü}°ç“Þ¸ÏZ;ž«{t xt©Q¶ZÅ+nµ»wÛz”U«#,m«Ï·»ÑFíï]®í×½ë«ìÉr÷nÛwzz÷—Ïi:w{¼7»36ﻲÜ7{Î^Ð4yÍY³Z@zÝ”+:ÀVRîíÍw{͵Ýídz»¹¶ë¥v×¼èõ†ŠÄ‘ osw—¼#/#0#,••W°5N„èP14¢”·½½OKšÛ&ö#/(û{Êóï·ÞùëÞí÷°òt@}zp”]mkÖu¾ƒ±€w×(Ø#/ôÞ-¦žÞï@gw½†ñÜèJŸ]Þ:}õ{kzç¬{ÝÔ#0”ß{Ǫ¥÷Úö^.Ëí Îû¥—Œû8Ùm-ºí·j]]7·+¨¶Ùí÷{Þå¹·ÓWžœwQ}êë>Ë&Ý]õÂê½µ'ÓíŽO‰ÞÞÙOí=íÔí»®9ióÔ½Ît®jîgÏ›ëï{¼Ù¶Ýw[}0îÙÜX÷»­ë^ÛOq½.ÈSvÛ.iÉ¡²§wg¼Ì#,{W/yÞƒìôÓîð#/îÔ¢”¢P#0´›#0ÐxWh9¡õ׬ÕíÝÛܦåjéÕ°YŒží™Ðõ^¥êí#0Ý.x{°KÞ½¶Á¡îíµ5îËÀ#,;zêy#,{«—#»ÎêãœæÎ=ìÛ¶wgKe³œlyÈî(íÍ]i ºk kâtôyzî){ºí95ΈC­C·ÙÇÝ^yõëÞÁðV¸ïU—¶oC¤òõ)í¤kÁO6övaϯ÷³ë¾³Þö×/o¦_ÅÈ÷}»Ã“Û»»mwÉ×Þ×>×iµvï>éó+[´f0>‰iô'sŒ®»uë`™´AÎ<XïZ®»]›ÞóÝ^9žÄ#,€ëVÙ#,½½e=»·xèÓhÁݳ·§}Íõ÷#/o®Ûkzw½+}Ø=·^ó)ê›Tù݆÷¾n¸ôu¯¤¨#,#,•µz:xŠë6ïXsÜÖ#0P!Ý¢æº÷­—p/`7gÐ:z ìo¶÷uÐ0#, œÕö¶úšÞ[#,}Ø­ØÒ¨+¬õ»Þînìaé®[öíÜÕÆ·ºÜ*õÞôÍ®vÕÛ;£’±)%í·tÛ$ÖS®îg½Ô÷Œ»SnÛ‰ë]²oMÛync#/|ãÇa÷ÎË»²¹·ÞÏ‚>ÆÙ†F·3‡6n«¶åGж¾í÷£ë˾û¨}¶îív—›_{¾­¾O½Žù—ðÓD#,@#,hÂ4#,dÉ©…!MG’=O(µ¨=Aé=2‚S@„ @&1i ©ìŒB&54ÚA ÚƒF@#,#,#,#,#,$" ‘L€TýOMTýOÕOMˆ¦=Êz˜Œ†Â`ƒ#,“Õ(¢AÚ¦12'¢ž˜DÓÔÈz#/ M4ƒ@#,#,#,#,#,#,"H4Ðh#,™4#,˜4‡ #,§¡’e£@Ñ #,#,#,#,$ÔD&€@&M4ѤƅSò§äôžªzE?#ÕGêe==1Iå=M#,#,#,ÿõö_çÕZq ÿRÕÍ[?b«]Ԧȓêª×p6dÑ£ ±LUEŠ#0x Öcïö~sù¸ÿ”~qDIRMQv–ÜV.ægÑss…‡˜ª¬QŒ!òP‰þf¸ŽÃ Ð †$ /#,xvd;ÞçºÈørówR­#0쨰’RœM^.+Æ/]ÔÕ§|¯m('ݺ¾&âñˆŒI)wWUi«V6ÛIk[¤E$ P¸EU* ‚º-`I#,)™I ®@$1‚‹xD r $ zâ´@ Š@@ˆU# 2*^ ª"d­UL†1™±¦f–D ”Fi4lÍDL•–2"ÊQ¥ÛI°ÅLÉ4Œš¦¢¨Ða¤°SHf061’Ó( F¥#,i‹*mH¤Ù-¢))e¥)µ e¤ (& £61­’¨%¢4¦™„ÔŒ”4SJ‘#)±´l&´­´¶Õ!Lµ Ø“Fbd„Ñe’´ÚÑšZ©)52T¶Á6U2jZ™i,˜™%EFÊÙ‹%È£D‰&ÑJkhØ`…‚Å!¦c4¨ˆ5)£#,M#2ŒPÈŒ’l6JÒK"³(d HH†’šVhɉ£aˆ¥‹RTX Ô0©²A5)L#/ ¤ÈIJhC11RQ¤KF’i$ÒbH±dfSS¦@ج–± °Ì©&1#0lk&VI&*6(#/D”D% )±A!0”1’R,²fdÔjeDFЛ%HI0"Rl„IJÁˆ1e™ƒfÒF&‰‚6 –$ˆ¥°”$ͲÌde,–1²A¦™bˆMŠK$lQ*BfAÊX(ÐT³ X“BT’ÊL Æ£IšEE ŒÓDMT#@„Ë#Idb6JL‰ še#*4fk)˜¨6†S&I”Œh&„†d¡1@$Ê™šÓÛ)´É35"‘#0AšÊZaY"ÀP’D”D–,›6FÓ4kÈ)3$d¥‘6m0mˆÁLÉCfM)’†3e4EJ$¢ÍK)I”RFLI‘JɢɦÊA@Š,V RI‰$Êj6M"FÆ„“Y¤Ãh4lf “!,™FH£LÈbH%›%2)DBZBj›6-¡2 II£l‚e²)Ècc ”T‘Q`•Œ¥&ÆhhI¤¢™K"%0Å,±¦X#/F‘(Ê2Ñ5jmkFÄ’Bi”R™(¤™‘´%±)f1I‘”Í,©f‚ÔÍ `”RÄB–2‘¤)¥¯â6º2+HHJ”F[¨ÚÆÑQŠ“&YQ#/’hÐÒÐmÙ&£k"0´ÉˆdÙL©#YLLÍ3Y5cJk%!hÁJ,J#hX,™)iL™3dØ,YVš4ÙB+eHªU+0¦ÍŒÆRÖÅ‘Ù4ÊÈ•%™µ’¬¶R”Ó#/¬ŠÈŠÑ›KZJe6ÆÛ¶ƵTm´&5%Tm6M´M!TPT*űa›F¶l˜XŠ©#0PJ)ÑFÔ¢iµ+¶5"V1i %´’mkfÛiبHL¦TÕ“LÙ&–‘E$mª[J£ËM,¦ÓY¥6©¦#¥BÊ$›if©dTBÚ[2dÖXÐÍ2É­-K!k4ÄAC*4‘Æ(ØÀš01 H† Ð˜ÀËQjIQ†R¨¬“M&M%@)³DQR#0Ë#"Ê$…#0J6S4 T$¦ƒE¢¦HDRÓRb1¶,Í4"i!€ŠŒ˜ÃL$d´X! !&ÁBlQ¢K“4³$-L¬Á”RcL&T”•†J3*‚Sf’J3#0$) Y›DE3hØƢŠ²lÐ…-4…FC"4hÓ)¢JJYKBd¬a”Y$± h¦‹TlÖŒÐj*Yd٤ͥ“hª4  ’ÆAd’ÂT•¤T¦˜²AF[$¦Æ Û0©¤ÒXÍ%iFa¥6™µf34ÓS4‘ Œ¨" •4ÁƳBŬÒl”Ä11’’6£T&£Q¨ÑFØŠŒËa¤ÃI%#0IŠQlQ`M ¬²É²RA±hÈY)f’Ì hØH¶-Q&Š,µE‚"-%Q°Q†!aŒU)BÚ-”³,…F‰"©‘¨"-"ˆÊm¬Š”V+0MLʦ2¤¢J–%%ZƒmJ,•Œ)Š™¢Ù)+Z”Å%)¶CY,Z%›2 Ê‹ ‹LRQ’#²a#/FIA­™0b›%)³6&"•EQ™Y5’)4“QAŠ2š¤¶-”“E’ÑERejPQiM©MQ&£Uc)‹I­‘#,ÚcRQb‘JfÈ̉*DPlÍ™­b©4[F¤´Ó[C+b¨Ô…#/h¦k4ÛE£QcV2[Rde(²²±6£‹2ÆÔZT°¦ÙFXÔĤƒeFÅ•KclYš“¤µŠM²™š£Fц‘QIEm¶m¶šY,*a1&ÊM,©&Ô†$•b´[bÓ5mÖ³i6i–¶"ÉmMM²Ôi¶‰FQ@hÈÚIŒ#/eLÔFË"¢#/D˜’L¤™!lšÑd`ûüpÿ±þÿóU°þÔþ·¿ØÃGVjÿ©*«Ü†©\L·!– ?Za#0A`ªÂÊ`‰Sïp†g÷uI„A#0¥¦?õë‹.)ýÞòˆhó%qÿmkˆR (1:7"T°\0RH9"Mí¨27üZÚ%Ç&ü™ˆÿV ÏþŸuú‰%#0XåBV¤VP´}Zê“7¯Ra"‡|Ý°ÆôJͶ O[¬FºŠ¶…ÛRRM8nైþÉÜF£ŒœDHXTÓerµÇàW.飮ÉÞwA/–øñwßVKÖåv²~“©»¶¸7­ÇÏk˜«å_/EsoEÍÊ‹ "ÂfíºãD¤D…æ¡WRyÝ°Ëå¹²3zÜÒPX“6¥ˆ¬xâsŠªzoÿ…)ýö#016VŽ—™ªú»¾¦Ò;¨_‹·‹É¢#,X¾¯ö]áĦ\mâب¦¦Ð¤Ruß%cýndŠcJïVIh7DÇ#0!‡×¦˜À°gDØxº!l#0¡‚º4»ñ+ÇRÙúŒ!¤F,ë϶$¿÷¬a +Pì]ÄSñëËÂ+œè»›™)#0(¶H,X¨¾é}³  ʨŠOUQlö¤ñC#0ÍÚH1±ŒdqÃŽ´õÒä;÷©›\7¾¹x#0MS.‰mAF#i)&Kk±ª4Š4dƒ{œlË ÔÁžîb¬9bÜZ¨¥a7"rpaæ2N»¥æCVëºÿ^þîòù^ÔbÓâë¶F¼•Î—|}&ðE‚¾ê)ó©¯ñ8Ó4ÿ¡éƒø+&œh+û¨¦n“ž¸µ‚ #Š†À2õA­Bÿ¼;È›Éÿ9`Úœ¾”ææ+ƒf²Ù"aYHQôUtÞçS¦¦ïÏÏÍ5ºš%¼Ö\g²Ë ›ÓŠ:êd|>ÌîÀäd×ÂxxTt­´¨kŽÊõ% ðª‹>ŽñyWRÏ:í[p£“5B¶ ¡c„#0`…„dN‰”ÆIš}Ö¬£³z ]bE#0бá’íʱþÊ-“±0””ËCºÊPMŽïÆçænEæßlMùó”ÂH#/~Z‡Cf8¯µ—o~”ã+¬,‰·6ïóÕ÷y×Ã~ƒ¨d˜É/»®¢‘ŽOmŒ0éety"60ãFK”/üŽ7ì"žtw#˜”m¦Ÿ»¤£²vß^߉»ôW󮦋!bŒkÆæ’Æñclk_éþ×{1§é;&·ÂãAëlc÷=ß®ƒ¦j ¤%!V»ó~}ÔQd#/)¨¤ùèFX pqŠ3Õ`gfxY¦F#ÕË–梬^wåÞûÞte(ØÖMŸ7rÅoÛšëåØü½»aÓî}]·oŽúyä<ÅŠÅ“_cîo¨,–ünÂ5ôîÒ}:û¯Qž^y¿n´X2sRê’š`o¥Ï|Øýµ™(0ˆÄWJÚÍóBa&µÉ¹éKB\b±•@nït-$­è«h0ZJÒ¡l5dÂÈ¢ZP‚ÒBNÚ€qÖ”Ý ¼U ¾J)”©.ªÚUÛº¬R7ïמC.ýõnï$,îí»,"£sÊíM˜j…¦ZH##0uGJ» |¨¤:0¦M2Ò]5„²ór•S¶Š?‘”³‡ –ÅQŠœûjO41(Ü ©äûò ¡ì¢„õ_<bÃ܉GU”Ûrï‹æj°ã‚Qý®{çˆt°µNìUuâýxaÄ}QQy45Z5Ú” U%%0¢diKñ÷)~í=ÛSÔ FB#«ŸûYÒû.š>ÏTņÛá]Åë® ÔÑ)#0H‹@§&”~­èü<¹ß¦èàQ]ÉËϯ̓H2CF…lVÚTSo=²’\‰cr#wT#¯ØËa¢È˜Ù½AjC?<7wÆcK‹õÜÈš`£ÍÃOöktïªv(~š®œª¾ŒN/Š‰£GP§Tþ.wÏÞðlÀlšaŸeÖ붼£é/–8ä®fRÌq=öÆ$ñëÁµCݺ„r‰Ê=ײ–’$;â†q²ŽS™ã·mH7Œ iKx6qÑHYó¡Úýäؼ™Bõ:Òd0³œãË:vÕ};ÓCYÞ£é»3áï鶜­×X5£5I”¹&?J®åE·*§èÐ:a|œ•êö’Tƒ‡-jÃdkÛ&23쮸3³'©¶d±ÍÛô_ü}¹4 ‡#0ïºg‡fq„gb^ç„ùçÅ—"œpã=ºÑ[÷Bac:äC,dMǪf(=²ͯ7¤vˆüš+²ÅIÜ„zúe1¦Å4è=Fj/T÷D¢~Hq7á—ëµÍ±‰{òC؇o{³áÇI1ÐõUñç2N,ïΞ8)@èÐÄâŸ9Ê°©ÜBÖ³-ä°‹tæbGOÒCùi© :Îz<:öI^Ú¯ÞãÌe;Ì4Ï0<u/ëâŸFé==™ËÇõù_ŽÈI$‚±Cïq?HB?:am1Íû´omîQ5ya‰Ü "¸‚Qƒ…ûN9âjì›È‹„Üi_d#0?q»ÚæaZ<þ1"4ÀÙÕ–f÷fjLÝ:Và‘äͳO…“Å·#0¡­h<1H§˜#†¦…z~z>wÛžöõnlæjûé¸×¦fCd5DTÝ'Ý'Ï·Ÿ§Êüq:wÞ0ä€@î7DÁfHÝJb#0N$QK︅ÍÞ÷PKôzêmEFy4,qì¹~m UKTW¢ª#0(C•!¦¿‘Ÿ¯üMÍ!ËèÅžð‹ÔU&ªu”$1BApTôU¢!uP.¨_{="}nm~…W<"}-Z‘ûN_µöç P>ÝÒÉ?¶Õô[¸K¼G ³­+¼Þä\ _o桞ȷ5UÕÛ­èÁµ¬Ðk&0ŸLµ»Öf>«*ÏùÎÃÝ¥,ªEG£B:ÔŸJtpŒpgwKF}óMN|N \=¥ü CáW3ö’>"#/Œªû*Çr—ò‘<Ö½z#/4™(ú¨¤A_½0;vfÑEØÅXØÓbm&ˆÎ²ß[-‹á)ønhÇÏ5OžýyÁÄdA—Ž½pÁ†8Ìm®ÑD/QeÛ7ë»e1å÷M=<Fúˆn:9ñàr¹»jkA@îíu2•¹^ ŒT]~º–Ç é;wù=wÞÎ4;#06Ž”Å"“3Š»ÏèU&KïªGƒ5¸S×»k˜÷Ñ{bÈ°ƒèI±ÛZFf"(¨²˜Q:ýÒ‚ÛzÕRˆÍ:âÞXè‡õqÓn!­és9÷ãV)}h–ª©ŸEËN+É°g7TñœéÒ>ªwx¸‡ï¼®™wx²vhˆñˆ±'D¦½Y°DQ0ïgÁ…ØíéÇNuVn´yu¢#0h]ÚD´ñaK–RŠÄ:&Ù“sŒwôм¶Ù#0ã[E¹$$?ïWçÅ8h^é> ýÎY]LrtÈç¿ù 3ÎsËòŸ©U©#PüÞkÏ“êàÓùÅ֜ݰ§­y±º÷DcÙêg†I!”¸pkhÖñè'ÿ¬ízw^Û¼ëž9Œ «Ø&sαñ­…ÒCìÞ>o—Ûø•Na9zLy#¦ Ä_‰¡í>ÍX(E`ÅT#/™~ÔÖõ^FÆ*w_¥çßótÒ ”å"1ýU<Ò‚¶¯?3r(Œùžõ¾e‚ñÂ,ÎiH¢¬àPk¦¿–L‹¡©¸xÞiùûÎŒþÎ[Ñûx#`²DÜ\¦¼¹+#0åÛÛ€¸½ÏåR„¥y­„Ù_N¯ #09~ Þ‹WçqÕÝ&”É3ù<åÉKκUU¸ëÑgi†Ò™o-8y=öÈõ„Ä/±ZâÝŽýZ})·‘|ÿg´1Ÿ‡œÍòàè|ËU8°ÿ§lÔÂlj™²ïcÌs.a껧®ÿd|µleÜ5Ýþ‹•¤þ‘꿆œè¶ßh×_R2¸÷ñ‹¾$‚ÕF[µ2ÛñPbˆ,zðeŸÇú´ÛÝŠ'Ðüx«‚ÎÑ÷8NÍ‹,Nh°Î•wv!¶_Ò¯œöíϲ‹Ã²¼kÅ`© pŸ¨ÕQ¯;Ø}È &5[ s==œoÂg–F¢lÉŸ£Û}4ª"‡ÍP_Jì¤Ðm¦5P•XÔ±DÆâá4ØSòaå½s‚éHÒ"S‘])§ûùÕT91öC?¦©–9¢ÙŸèª·†subh4”ƒ.ÅSqç(’r˜ª)ç&<ùAV˜Ÿœ÷Ø÷Å|LŽ"<lÉæZ_g…W²Mþà Åל½~—ÝÇÊûïgSæy;Žî£òëaØåPô祃¯O— 3[YÁ¤-™g¥Ÿƒ=.q½µ‰^I_#0¥ÖÍ/†˜-Ju’MæzTasm2L¡7mßÍP!‘{{*ÍÞ7D­w¹S ZþÚúðyQÚÌ‘Q‹ŸÉÓ6õJª‰K«¢°l¥qpçÖƒ•@«ðW‚$¢}S°P‡d(ÁOV "‘â¡Qš-ètrª!Æ©,HnÕG¡}Ûä\Ž/r¶åi©}2y @3øÝÍ–h §žì Œ¹#/xL” ûâ&¼Cîç4Ç£°ˆ!~²µ¼„!_®Z>Í_HŇéPÐ/ÈÓ®(8¦ñ_rŒä¹û3‰Ö¥Cd®t1])ÏÙ#ý\Fs0cß.fF³ãºc1ÇRª…«îéa€UDYéON¡\óSߊ-Ç×R÷ÅßÜh,Ós1l@éEùê~_]Hìê12=Ϩ†%Å€R1}ÀaR‹w‘~PL®ýq0—'LÕZoÞë³ö@l€Xøo3­®)wÒÚÍJ{ª§Tfô•ÚÕ  ì™K¾Ôs¯Ã»m´&à†ÉºbèµF`L!@ˆÅŠÆ v^i¼"ã-ÅP0¹(õfŽiLÅ^q¢e…}­O™Ïà3¢0YµUDÝ%ú±`«!zÕtªba¢“V’e¡g½„ nϾNÕÚÜAnèýé⟺%½ak,õeÓÄ6&YÃŒ?A1¯^µ˜[·prþvW‘iÌJ3p>»Õß\NF8eÕÙ^Úð²˜hÓWt‹:5.»¨J>Ы^6õa™•#/&Àü[ÂSË.SˆÌjŠ‰¯äL• <ƒQ9©ÕÆÙäÖk+­ˆøYÃLLkQ(6H(7‰<÷ö|áWÅñ‚ꔬ©«ž<ÝÛ›Äír)qÕw']eçà±B*¬Q kLFéoï÷øYé¦~³Ûíø —QQŒÔ°íébIí*I'„]l^S¢Ð`ŸTÿòEÈI™Ã÷sÕ£\0Ø~ãö€&€ ÙO-çiÂÎñ´{qX= ”((ŽU§gû>)2rŒíQœöçQä‰_«‡ LÿƒdœÞñ9Øg^Ý*ñ ¢§šPüPin°š-íÌT‘åouDvòŒ©Ll›7¾JÝ-°í;:¡!šÌWXÈ5Ѿ‘§ldiªÍÜø«7º™¦’—6PÀîgá–¸iAºD†Àîù¸¥Ò³»˜^ü=ìû+­ ÊºzÇð†¡J`} ‹½q¶,ò Qä@‡®µ‚úZGû[UàúÁ­b?>ó\7Œ2ß„Ý­‘»ñF…’0/#0ªîõXüد…… ãVë,€|35ü±¯573jsy ø«{[òn#/ñG}ªËþMÑ/q.!Uëk1)™ múš®]:Æâú:Ç?%NóÊò06îTA³¸UEÏb¼*g•;Þó†§ÿˆ¶Tq(Æ.©‘Š‘WÐWQX²|ž¾Ì„šv³¤‡Åö½*ÖËðü³:ì‰nFá×vÞÇÃâí«¸Þ0¹šDéb#;Ì÷x<§n#/_”2© ­ªµB+½:kçy'ŠS#¼r‰d­í1Ö²ÙÕ¼]Э¤¤ÊX}¥š“néJÍ¿n1SÛ’Hú­ `7ç®»€m¡u³Éüšå¥ÖO»qñ1¢°>?ªƤî‡ßˆ ßÇN^p¤o—ûTÚ)•êÃ;{o|fȬšQm7§ïÍíéï׋ºZe§Ï›¤B#/âº<¦îï.è‡KŽ$ÛTÖ¥0£2‚[qò1@(`¹RNPuÛ‚ª0úZNtMeuf{v¿#˜¼Š…ÜW£ËËi<)È(r‡¹¤dãžÆ'À‘ðƒcQ©ªP÷¾)q—)¶Óâ×be+7Y=w¹ÙŸ#,$¶ÃÒ ž'Á·>ÇEËïåã®;m2ÿ$ÛÇîàÛ¦bÐÁ‘H–Š‡¢Ç¯‘QD´5wX¼—ÚÒí±YëF\ îÙcAÅxº5b;Õ¤¹ŒR$gÇSvCæ˜éqÓ Uwd×m»v|ù„”œÒÁnÒÑzÌŽêâ7m ÌÄèn·oMn7lßc uÔˆO¬O倳§³Eá4@•E(¡LLƒðVp1#³j¶ƒ+ô¸0ølæÐàÃú‘K› Öø‡aÎÄÇ%/¬·úm”$+U)ƳÃi®6ü}×À¡O¦dŠ@ˆÀ÷Ð%ÿÊv¶ðfÐHÐ9wÑ;EÝñ³ßriíåÿýúéÇ—UîÏýäg“¬˜ÜÛf@§]H 1 Rl ó ññÉ/$ÍÓÖ³ÓZãåÛÚ_n?63…ªp·–”0³32\×Mÿâ \rx®U1í¾á×›˜êí‡#0G?Aô¬çÊey?Ivôñc¬ð%^pÃÛ°ö3æ|·ÍKçìÏë›è#ª?g=Kmk·M˜-£Z’߬µë^£•ÅgֱʒaVlE³ã¬/÷ùd­ŠãÒiµD"[¼èÖÛ>4χ& ’$<â^(¬g—ß”ºˆ¹ÜÔ|ñ›ñã)Z£L”8ÕÌ:ñ '#/#uX¡¶(:xÅE‘j9*¹ÕÝ­wºÂ/¨H0#0Q–o¡Ëk©6³f…9èdó¸6J7¹_°,ªWXÞD%­˜8Œ¥Ø#/ÂéÓ[œÅl,îw#/Nš½×x±˜Ô7rx"g¤?#/^Ëòùöæî#9©ß8Ï»ûÃô'üfžÚ<ïËÙÿVþßw¿µÆˆ½©œ8#0žFõ{É”2ó¥Ê3Ö D¡à¨¼£¶IôŠ¯‰#/ón¬ùwüxíñ#,M›ÿkìK‡Ê‘ßîÿF#,%#,%"A/ôB®žD 6©Ê0]Þ*hµ–IV©¤¥½úÁHqt"<à#/2ðâ4g®iT»&æ;|ú<~’±ËÏhÔ#,OäTCg^Ÿ5Тžo£F—rÔb.´„ÝTHÑÈaNÐÎ×=Çñds#,L«5#/`B$`,€?«úwsòýû…ß³¾¸ßSû¹_/„ïã7“Ü{çÁ'Å bÚ{›L ÿÆØ“‚7#üÚ¬rài ï’"QâŸÎòÁŸöôÎoÐÕÉ:¢)‘{<êˆ`I ž£ÕIÒ1W~•j…ë@òâL°i/”@x¹ãZGUïä°šíômé¥ÿ Z¡ë„÷•ZÂz‰ç‡ö>d³GÖsl˜¸:=!E”P PE„ (¼z<£ò4‘áî×ßÀäŒßM*»À¾F0Ô Ú­` <4^@H . #, ^úR4sAâ#,wÆ¿#,÷BÚhôÇÛêÏ\³Óf¿ãú#,ÿŸ$>$|ñ5„aznà±ÍÿOÞAц6•‡¶½ªeŠÃá<ÐÖ¿¯ü3÷ý)†íZÿ|¸6#/»5Ïí–ÄLÑx:c-5]Q÷6;}}úN™ÿleøõ»Ùäx»mV`?²þ>šÔÌ^Ì°×RW{°"qÌ!Ÿ4iúÜÀY:ÇðÄÕ­gmÓÌ9=Ù´Uªº|#øÁøÛ°ŒÏ§“[øÚ_¯•ÎB@ÐÕ¼%2²áh²w4#/¾¿G·M=}Ô]¹M@H¸–È;Ü|×ÜÌn¡fŸ;y_ƒ¥fB°¥’?iÌÙ¯×1Ïto[%úYõ¿ÙË:Œ¾™jÅ„+sÁå.¡`¬?Ø»o@¥ê+ êìuuÓè’]|P00€VP…Ue{8ZˆRZµZÀ:<ñúDµf®¿˜y+¥Î°›Kôü¸ÅݶØI­ýÚ/=!­êŠ¯ædJ{(Z÷‘þO–öH˜c+¯>ÔJ(Ûb2xò`w‚ý³ì^s†fòM(Â3§Ñkù?{û°¥Æ˜vдãï±åý8†L!ož3+D«i%1áxª„×öFv`ÍNöG’E‹¿žC'@PŠ«ÅNTG„Тܧ&iïÆÈÎ~ß?mLÀ+ûþŸ#0jáT‚(Ž¢v\þ=˘Iåê¬ïC‘±¨ÀakÚ›;‡VÚÑ&¦ÃgÚ*˜Ïß<bÂÙ‡ºò„ ù¢³GíµáB+².àŽý‘ÐàºBbb/˜ÚØ ¤­Ï£µé·ó¼žäi6,##/=¹ûpnµé“Hå[=ú¸@c#0ÚÆŒÉmo"õ(ò¶œ§GöSšwk9ÇØÄaúó&õ¨û8¾z}¾ Ï'~„ÙþyØM»(gI¦‡æª—è –}ëé¿ëa<þ|ZÚáå}Ç4ÓŠ÷çkô<L¼“è¹cY¤Ü36¿H‰5ï|üÅö~‡Nš¾è;„‹›£#§-‹DóE2@·—!ÞÿK‹ºwi†Ã"‰zÏÙøÝ^DF ÉÞ<͹Å7½ kø,µj˜ÙwÄ°ÒMv&…{¸a‘ßü\$6œ45Jªi£ZFêZ£Qº!Ÿ»žü~¬Lüj‚#/ 9|BghßêÔ„%ôˆc„Ùº¸¨L¬{ã Ä]­å«É#\­uFŽEh†1ÝûgMñ7›¯î{ºZåq.F$D¦÷;Gi£ú6ÞÇúÄ’p;|;ðPe`ttGŠ«0<#0®Øpt¢™‚¡Oì}Wßð¿pßWÕû®†HTXB0,BEä‡c\I³Â­P™h6}òr»ƒi3LgÛ¯ª^ͱÓø¾Ü+kèmi¥¾n@±ÙÉÒ‚LU°íî¸õ¬CÔgøÄ:8›ÙÚ¡ÙîyÎôf"™X†t‘6–³­í½wø}æõ∆m¤ú#/Ä×Tê™ ¹Ld3êXÕÔã¾mdb!«ËÜøÞ^‘ïi1·“^óÓÁìtšPƹåjé<æ}×PhtûZ_ÉL˜=±Ž¯=´]ãÊŒH‹EYI³>ƒyÄé籊Ê<¶°oñþ?å#,cja„6t$$À€êÅ@¸øéŠr| ‡ó# Šò^sz¸çNÐ0ç,aèpŠl«ØÍ•è¥#,°B(Ý@‚à @M‡ßüØ"ƒû—OËm¾"•ÒøGº|æûk‰ñŠ¾ðt¢§ˆsÌ·9@kn±¤J ûûòg¼Ñƒó[¢#®«3ÎeÁÖª,‚¤êP!ËÝ@á(ç¬5؇Ž†C±ÒÍ:®Ä2›³¾“jÜB÷/V;hÎ/×Î~Âë]9†ågܾì´ÈÔ¼xï%`Ô½ÇǶ»ž¡´ãÙŽÄ݆?#0>Çšps½Jž#0ö¶Ì™²°+a%IE +QÉ–÷)8¹`¬Ù­…iž¿y³“<g¥¥¼ƒ„§tиøYE‡•òå›r.Q×Éü#ƒÐÌꆄÊJ[·’¶6 tb!ÍåÅÜz|ùË'<4C†w:BñwœÍV¦”n¶‰CÕeÆ©ñÕô¸Tü¯„H6_Ç]gÎp–:×­ôcvy¹ F9v©½Œ;•±·¢5nu'P ½dÜÛd3j]1K¢è%2:­#0k/OLU—6ú²‹•ä÷îã>ÿy ¶ê@&›K0?áï2eÌùÙ-WWùï!yþ²D³è——Iî–¥a+GĸÌÍ*€ pÜù™¿›•^šTr–×%±Á“Їúlß…iŸs#/Ž8°¶p¾(²Gº¢ñå\òa{µÝ狵ƧŽFb",st.mËFÅ'Õçê{~äâ¼#02¾ºzëÂùÔ¯Käßê¸A8f#0C:fHÝÓÊä7—»Ííåê’nÏHõg® M:!q)Di) qN„2p°a¼† Ùððï®y›«ÏœVÉF†ÖHØv½ªàÔèôrBi»§ 3ódè±Øì^ÕQGá.Á¹TFËNÍ Ðš±ü¤’…Å+c`X[!Š½±åuÍÛeå7“ïL;UCšSós¬ÙPÁÂI)·fp/2uAO7‘&#%#,À{¡ÔWN-­0B†TɃé #0DùW}« a‹„C&uΉ6¯6Gì1“H­þ’ojœ]vþQ¿¯¤à¥—fpÏ+úìÉ^yÈÕµs½ôÛ fLןo¶Õœ?XÇ­#~Œ×3Á·¾&‡cZÆõ?¹©‰£C5œ*јτš乺ͱÖÌRÐå&☦À:+Š:™Á.ÅT4nL< ͤP©PTˆi¨u¸ÑŒ+`0ÃáhÐCˆ3#,ÑZJ¸!3E ÎXÛd1‘,䉇1>»ÒO——ã¿ ÆýuÈ£gWÔ¶á‹x®š½bÕ÷•çÏ[rئE©SQWÀ4”hm%bá€ìÆ2P1‰dëk–¾VæñôZê[dÛcfÆï™åñêÝ>x8ÌîFÿn 8¿¬ÀVÉÇ\?qËEŒl`Í:Ž“Ë)g¨F·‹ÙüÆP¥¤‚µ‘ú0ñ ÷gÃcÙõ°WPª7žƒ¡<;#/s°üNί¥ý§ª|ú“^º€Òáú…®„éŽÑp|`j^í¸æÇo[D#0xªu–¥]NK2ÏòÐy]äæºó]ƒ\Ë»B}OilPÅ:¢WQO2w°&v>W{Șp\p¢ZKn2´EdliIh7$—Ö¦#/}[ž?¦¤Õf+Fª%.éTÍîR0¸Ð³Þ¡;µ\q}$‰kT}úgåP~u®1±c›>c…¨Aa›$H#ÃÖÀIñÚ(Ò¯+ "ÍõÊQUwÈ•L¿½U0„é©—’vlŽwÚêF0µ'††H“6ßÉo”+wÖÒ5ƒ<ärÿgŽ·²loîË¿¦ã5~Ý#/f|L¤’Pq5ÑÒÚ.vi¤¥sºp¤ãü´÷Kq>³˜„A#/ÊD8êØÑ‚¬ìz³¦ ž?Žð㈙zX¸}…f‰VMc¡ºêtì«ðšÑS*³Hƒãd E­êÚ¢0D gæ=§ôàá8äm…›þœimð–¬´<¯À«ŠRGœ5…E;%#?ò³ŒŒÛgO”¨Ó;&·7u‘cã\4Úµ|rmZ_áèÔЄˆ•ž™ÜÕ‘aÕÊósB(©¥4ŒÖIC"Ì'V”¤4'Ö°lZÉeJFÊ”SDÓ #,YRE±˜C?¡h'2 ¸ÄV#\²A¢‚Ö¨o.Ø…M¨©4iX,4 £`Í&Ó4<‚€ÍhÓQ©«#0x‰(wÉa#/Hx²Kú\ì.öù¡¦°ÞF‹= ê˜(qù«#,Ó÷¨¡§njëÍUwÎ)#Ⱥb(Ò<K³<;G.’ ð›véšÌÝÝÞÊFÅ\徺Į0äN¥&ŸÍÊ‘uGž#–œ©˜fŸÌ-S(Çõ}ï]ìgÏ;‰†n¶K-ÕÍr³`°Sb».¬ÖfÏVoííì†#/˜ì-4ï ›ÍW)¼tåF¿3rƒjq9Ð0I;c­Ó2ÅÇ‘ÖpÅ=hì`•jî[vcxŠ|¯ù^¸Y)2'çURœ‚†Jl~åkx¤ØÆ(QKSoÃèw0Q²÷K¥O“ ±³íM€~3µÀotgRÉÅŃCØŽ2V €¾qÂå—jI&ËFs#/ê‹Þ&r_\xßQ(P^@'3 †ƒ:Î5©ïplfÍ„§ùvîLõ‡í_¶bøð.ÀiëRM¦EFR¦yI¬v×K©EJ¸”†¤c´GÒò—¦Ï³~KnœŒN9:cKk*RDÐî×1‰x÷â>kºÛܹ#,Cêë‚ž ââËaC?ÅtfÛ—&U¤ˆÅ+5PÅ1{;*ÞËš|ÎÌ8„>Ol°Ñ#0¨Dë‚sÓoê×N#/,!÷=H•<`ù_ T÷bŒ9ñÇ¿#,©°ÿÞÌ-chBáŽEï6gl:ñþÎwý¦Ü@€íŠ¬b?nfuŠ‚J– öiÞ°ß ŽaMZ@U:ɬ 7¶ ¿ß‡†p/'Òõ+ᯡ"Í B~Alm#,ÕUKr­t©"×6¨!"5(ˆ‘ U$Òät¤Æ2Kg|ˆE:šnŒAÃ1‰¾u'^Ó3kP q 'O¼.V ø팱{?U¨å`F¡Ñë#0Oaì=A<³€ ÷ %#,3[ј;â[Xh-cãμ9W€ð-º ß>ð…òTä_[×W­Î¯Û ròLWçjRØ ”8fNjã4 ÐhóöêüµÐjíßúnæéA›jù”B–‘Ry`*=•IÛºŽA/$6xÿ~ü¾-£>øÍÏé½ÜÀØÇÓ© àTö&,#0±V#0 >¼mðM+¼‰ØU½ã#ÿ·0¥ÇöU¢\Æv©ùŒ7T¹&Ë_RT®È¤eT­¬SÓP¡IÝòÛÖû!áb´Fþÿ˜6nµHæÇ°`’Dz ÝóÃOÇ´f#,Iñt=£VïÝ¡ÝuU$›6úú¹¸Û:ÇVÞngý;,Ÿ?n?4ÀÙæ³<65l<~½ÚöÓ³ï×鮽—‹ ×xtE„F8Äl=šYIîjUà30qrB;™$)âÝX·ÏÄy3Iâ‰m£Ó;lŽÏGÍ¡¹8¼ðñ¯$Y‹*˜S†XÙp·_ÿ4F ú½Ÿ¿+÷u 8Lo‡WPUCñÏR¯%â–Û£Š¶£a–6Ý°¶#ìý{?2#,šSGWþPÂø?–ÕŒjMŸ_ìüÙø€$çE P–ñìæ²ØUC#,ûG´¨ï¿Øš¶ôä)±àŸ™žÜI‰ü'–nEŠ€¨)"‹>'kAÍ©,Ÿr¹“²¥‰Eùœf&6*þBø;nÔQ¬·s®Ý5s^#/øq+(‹ùï6îeOÕü…À} ˆM%dRiG«¨,¦¢å¦ÛÚì Aþ$,L ³ë¾Ë4bÕãA*î»[©n[óלZ^ŠÃj=SnŸm}?f‡ÐýR°QeÑ äñþz”’‡ÑÓíÿƒ°¼ÎÚÂ9D£ŽF&Ì‚ŸzRCþƒ$’˜w½Àßä¥4I¢R4È‘õêæûÍ«ñÿ;«á‹IµìÛˆm½I­õª¯%µ^ïnîîÄ­~TÃöf¿ódûS²íª¡-¦KgŠY„ L˜Pìî<Ë~…5/ß0S²ô'ë$>¢e^—ü¬^mÝÿxË4¾m:—÷Oé‹÷©Jg=g÷´º˜`ìB+{,˜c„­vÿ™(ëªo©Qo`Öàè´ÁýÇ ²¹éGâq8žQG8ì$Øeëö¾Âkߧ¤ëùhn 2#0èô ›‚ªŽhp˜¢„Y0¾²}ék††#0ƒÑã§ʹ<_#,”ÊT(D†óÜšoXdQP‰¨–¼BĪ{*óA×¥ãüa›{+ÐÃÝñIŠ…‡@Qéµíaù[HnìéU„…úªM&*ÄIúR˜KB\æßÊeåBæ`¤¶\è=hï\wùþš7‹O»á>ê¿ÿÉßßú'Ø#/>/‹Þ#DyâÞŽ†`,6yópãgîTÇø[!™W¬=<+¾ÅÏåÊä$j?YÑïØ"h>s]bæ³:åÄ.ã}nú$¯ƾ®@0¤[’±›«ó‹€ä 9m™"ðë›Ì¹íûiUuF®+1ÈdóE³¿4Òç¿÷AGêçå°Ø–!c²³ðû…3YõD.Ö_ÃjèzÊןÅݱö6ª['~y‡PðÌ Õg#0³°àí½HÛVøy7R_%TÏÝS#åP•ðcLî <»oR÷¨cvœ·×u´[m4ÖßÊ@Z#/À€kÒ~K ¬šÂÖ]!¶ä@ò»oÖ“‹³9ÎäÌïg=“C˜t®ËÈà\£Nù2]†\]ÖhC®#0Bª§½^&2b@xÕ.H»i!,)óü*XœˆúHòœ†÷ì{ÊoG#0ßD²¡_ËfÄr|Ö|wþOÐdúÏRÆ©Íy'åÏÌp.wâ£ØcX¦Ëëûûì#/IÕ–1‘¨Ñ»ó›iØDœþ=‹œâÖæßÍBü¬ô|³L‡RÓÓ¤éuyä9:J’œ`Ì;”Z*©¢³0ÎBâN*W˜ýß“˜?¯}.æ#¢õ92 ù¾ºàT¥ŸHzN"@'QÏ%Wð·Ñ!_Ú®LL;BDÚ×3”`#1¢#È™x>õX‰}ÏÖø!PP`À]Ì#,Ô7çÅ™¦ÿð³éU‚b°™5øZ3M¡ ˜Hl}yšzüa>Rf¶/6xCì=çaÑ)Â6/݃\ œįB°Ë‡AË/…kóæÏócù°ñû;U}:PØ°§ÈâÙ¹jêžur·:ëOÏù¿Jn<xšzf7ñåÏTþ¥aü™ÛôK€C„þe+÷ñê©5¯¸XÂ’Ø2S#,¹BýéÖ,ºB#/âŽe}ÙVXâz_hÈ8˜|;™™u.yjÕØüuŸŸ¯¶^oÏóÌùšù¹`zz¨=ü˜ùªùµ—說pá_–ﺿ|-«Ï›0X.º þ#0[Uuß:›Ôȳòö¯£«òÙ§uÚnkc•Ùº>Øvd0Ù«â¦ý–ˆQåyaä¿v|d¢vÖöþÓŒ1ËYoòkëã/ë×|3WC­á‰Ãf.‡&Ç_ºMåVÊE.êÂ?vù|7ýüí—¨X>×ìÇŽYù·[ò¼é–÷ÍmáoGuy¬}\ÓÏÉâÊÚ¾LmÀwÝ‹Ç“›:O÷'§ùÏ×ß ¾Äz òHè??Èì`½zÏ͆|òžÃ]܃çznÃÝÔñÅMrâ9EÉåçrxꋶWÊ·óÏ«t›F/kÿ([ŒïruS7‘ÔmK[eáøèßGsýNšÝ»›·“ùʲ÷æ¦Ã4;çÃFœÑ¾·ÐgŽ§þìw°ìG:žA[ºåUEÚ®æjã› Ê.6n–e¸,í®­3²ÎiîP5õ¿¥…XW-Yet͹GÉ#0†Üó͈(¸Xç‰çØ!0‹•×…¼4d\çs+šg¡É›Õ^Y滼œ7]ª®ï7|Zöy­DÛ,5üOò?W§lûu]m'Íűݳ;CÄ¡‡1`}5ÎÞ= #/üifo-rçèÐÕ&|:@âôñIX‰Çz¡aŸý®¬Çg¿;<o!óÍÂ\¯×Ÿ‡´Zëã™]ø<kù‚gO/Ÿ•z¿?o—È=nŇ»–«©8„ÃyØ)¤e^¸W톰ªú+¿±úsx´][vfOÊë"®­|c^v+è³dºpGç\lÿ¬ÈWö€ÊÑ'L;¾y;Ï« £Ó#/3—"úÝê¢XÔ~Þú¯ñì³#0(äý”HŠèœ—¯Åñ¶}·ü_ëüóâ=WÇa¿äOçîÍ#U~]‹óãtèÜù6»%äl¾#,žïŠ6ü‡W­ÂcEöC?‚Ç·“ûµøãƒZrBà+JX^k¼_W®ÏÚåôÖÏí­îv-oLZžÎIÕ9^¯íæö3½wɯN ¯y^ö¤$¿ìèÝøe™+r2*wÞ/‡³;¥Ë²›)gÁéþ”«æÒ9xapøÏñ>ÿ“äúÛ÷¿?nmUÝË#,1v>Ÿ<÷ùs·áú€ò+õ·ƒTÞNnöþ«üñaúYD±óoP±cTwyÑX‡ûÇ×Û›ÀÞýáR).ÖE¹ÄYÅ\ê 0ˆ~¿ƒØÐ2cÄ#0ŒŠÑ+(Vb¦—l\ÛÙïÏ´ ¾Oפo§gP¿Ý§èõp»†cðùý¾>ŒzD¼ÃS3iûúU¸x§¢¨h;ëé~æJ“æltgÓòg·“é5º2‡Ö½h:ÈÝ0ý·rŽÞ¿Dú?<ûÅß7W zGâ7qáÇÅÚ?xP·ëÚ~q#,<xhÔ€(ý»Îô“úÇpö~俈ù¾¯Æ<”Ëã•î#/|ºÛežÙŒÂc?‰)#/Êç#0Ý^å[¦=Ÿ°ø¿†¬Éì–qAÙû<|YĤ?GÃ׿fà>÷üçÊ•§Ofö¼ž{Êô½€è?Y 톶ˆaL§ø±4øá]žÑí-¢ìW¸·úß9òáèÀé߆¾ïÓ³Ÿ°nkãÄrÿ MÒö+ö·bŽÎ¾EÏÊïÝ=õL™/¼öÎÊx¦Š=àŽÿÛ󾸊Ñâé…>Ã=þI‹­ù5t÷ôñHh~ÜÃK¯¨SÓ”ÈØY#,(ÇiPœZ„$Ý#0*GÔÁ„kÍ.S£z¥DÐMÕ8›0@(ó—xsTh¬@2 åIÈè+«Xõ×fƒË†í¥Q°|aÃ×iƒ²Ë5|×վἠê#­J‘ÐÁ€ÐúµÆñ©þ­òK+¢cÂŽ5m]®¼‹D½3$°upÃ`†€2øÓ_ß»>¿óÿ‡¤&ᕯôëûÅÞ¾¼ð€$Ìv9QÀ©Û +äÔPÁTÊ}@¯{áJ¶ÉÎiS%aMŒÑÞ9!»®3Â%õc›'Ï^øãWÁ¶²óÎ7vL­«Fðæ’0®ª0✼Ž{g~":Ç/ÌõXëGHc€Ã^œØýGÖSÕ<“u9B£¶õ®#/Z‰#0ßd´´ÁŠˆ „“¦ºý¡i^°IúýòØ’¯<5G5é<)f!´æAŽm"í¢«#/”G ï—sÀŠ¸fò_WcÆh£ÇÆG—ÇeÖÆ¿h’CÅPBJ“(ѯd@pí:N2Ì"ö\Á ™|Ñ«Ÿož i÷åð¶ñg¿˜ÆB½~g÷¿=°ž>úº;æñÔ:½uÔpçOVgŽíYë„õ7etªu' )ø3Ùóqä#‹Ôéh¨óã2]?[¯˜ÃQÓ£@_§_.Š» m¦ªg]Õ~Ç/Çö4©zÝÃnjÆõÓ:DòäL™ŸØXÙmfÙ‡KâÙýŸ.ðã캈俷Ê57&à/cÜ4ÇÍ©µ0ð<æý6.w<¯åÃÐÏ÷ßñâ5ÛÆí¾{¼±!¶)’M5:³>ŽgvA°u㺪ÝdÆ6„ßÉø:•z/º]¿®yŒ|ã#,Ý (Ô(6I]ÍBI.áp»Iü|óWóý•¥\™†Dwy·_ˆuBd”òñËäófÉþå䆻[!¬¾Z€üñw~›>oòù8€3'ÌŸÁÌT¨°|®ÄkZ¹‘µ[r±ž˜©cˆ#Õ"ü^5²î£ÚÝkki±aA„Ô¢¥nØ€Øá‘a ª­(8Ü#0K!tÙ®—qd0+5wrš³,ÍTthX&°‰WdÇ`a¬sZM†™&ŒBÈš¡#0Hìh4ÔF2¸ê‘è¯NhÓ}dµAa¼C"’’oû´·½-3ƒIp3D$môE¡=X­2m:ÐþïÝ{­èï¼ZÎP  ùþß1ø,?a)ŽÊ1K‰÷ÿÌZÔrZ‹×©ÊHXvXEÚLÃóüyé/áý[ô|œƒ;;>ýÈ4¤mŒ±Hèé@ŒÖaëд›ÔÈ6,*Q¢¦“P„5žÌAšDP•W`¥‘•Gõ*(ZLÓõeôàžél·ãËß}’ïêÍo@—ÝwLt›mdoéÉ}-Ê)p°fŸ1®¼ùád§5+Ç~ª˜oï¦ymÍ–_¯_“ÑâU«ìú»ñƒÇ(ýoß»OÒ:^šÆ›ÓŸòÐ-¾#>N“YJ¾[ bçyúÖUWQÌo®—y£„{³iý¡[¾ áYFO•(H’A¿{¾RënJÇØ;¹|ß»ð_|¼¹ôxs”K‚)_œnÞà†` R­Ÿ^æËS®îÛËÑ”9¯]Ç5’$?­nþNG9Ì÷+¦æ°0´0îæëºǧº=ñû<®yÐ"ëþQlG‹Úí¤s¼þ¾Û;Ý>oO?ò³›³–]„Ùö"tÝ_é?·?žbÉ,€˜ò£~’ƒ˜\†Ðƒˆò{UE1?O`(BÏ*Å‘lY>¼§š3KÄÅnB“jÑ"¶”ÁÊØ¥;d§•‚£Z!¡”p¢³ݶ4«M«»µÊT›"’»wÕæ½5î£#0’²JÎl¸åª•#0¡QB¥"•Š"ŽÂ× мµc0¬¶võThyÉÊÛÖ¡œÃ@Ù[dáŒ+QŽ¦)q¶"2An$Þnçƒ0¤c]¹ DÜŠ#0˜)F 70e[1M$`áÛWUÓd¦±Œtˆlƒ̉†f*‹H|AwRÚæpI C†ùœ2P6´&T(Æ£#0ï´K4øofµhlbbÉeC…N"G«zŠÝ&“ í4¥­,EdÈ«Š1Et¢¨Œ*íL¨Ç:ë%#ÆÉä#'J¸f̸ø!U†$†¤S¼$=?D80W’Í'D½êÔA|šà,Þ%„"øûUOÖ†ÿi×ŪÇÚËIŒKˆø°±ÍIöâï—áN³Ô:(PžLõóaèú¦žßÎ×åŸÉßv ‘ 1™ÞxÿeÂÜè¬>7øÚú}£‡›O.§Ÿ ι¢ž!’A5¯K&ã ¢•",#/擹 „ðöxÓf€óHð ÏbüÅô–l£#?}\þ5ihŸ—Ÿõš?¸üç ¸ ¬°Ðç.gáú>y>@ÿ{T!ÓÄUÈå€^b9 Fgí)ØAvc¾™ ~m\“R%Â#/Öj¨9äoM(3/ÁØ\Ë™AU$’̪ªÖu(úà‘Ö/ÉZFºÑãË·°D8G¼ï8!úÍ9o}Îü~5à%! ü/ô á8nQ"ÜÖMÛ‡“Z"bñm¶Æˆ ?Ì‚ð§'²Û É7‰.ÿ^{¿[•ñö5¾Û¨.‡FLèÇó9Þ2hnFDގ΄fm—‹ý7%°Æz7^›+wøM$—S?™¾á÷i÷oÞûª^ˆ˜wV<C´‰¯?<¬÷{”.bJ)F @(ø s/õ³cMµHØÉâ2DX†¢_ÌÅlä˦ómðèìQ c±J¬Q5UøGÑý>=¾.zùÇ?ÕØÓ#0·â%OOºÐRɆÒ0H±#ê5͇§$¡\èš ƒº+P®ãA1«†1‚I‘¡¥aÁ­ÚkE¢šÁp3µ5º 0Ë Â!<Ô¬Ó­uzè« Á­A‘¢ãŒÁàâfhPi12ŠÔX_¡èâÒñ/^F&!êš™RéÚh>±S&ß!°Y¿ËAeŠM°¬jÐÝú͆¬o*Ì©H—y—ƒdEX2&u|3%–è4ÐøÕE{z´ëkU·Ûp²&JjtÔ:¸lj@lQ ÙXº,#/ŒÓM£aÃ(ÔíJ¶Ë´ÎÌDé#/Ðá1¶'[C®Ù`Ø2麰!“® ƒ²QršsgßøÿN††²<0c#/m%åj…FõÞË…×t/ÀëÞ‹½mÕä“Û ðµmøÝï#,ÝL¸ða&#,°‹"1 Wðy¤=@©J#,B#0‚,è·g/Ž#/=_Ÿì²ü¿!ým‹áB§Ž³‚áX2#S ‹é”ÉÎö¾Û÷GIM0­Päƒ xCžÿóórM8#húwÓ‰ÆÕ v©DópÚ~f òúIó‘ó~“³FZ¹uùñ Ð è°;ŒÏPðôqqtDe–AbŒ1aiSýpÅQœiwxc۬Ϟ¡¢†äxEÍÆ—#/"$JN×R#ƒN@‘‘"Fêa#/fý–5[ÒDæ*#/3CXcý}a£Dž›¼ÎÖ1m@ØzuÀ¡Þloo‡4ìy´Rà`#Á EºZ Â# u6U"Ûw079µtª­BÊœ¶óþ?¿'læ!8Ÿ:¦”ŠM»ÍÂhŽÍ¶#,­o§ÏüãöyümÓW·ŸõßÆR¾{êw7>a,øöä÷à;|ŸË¾uuÔÀ†ÚŠ×Ýü Û‡ÞÃCÅ—Ž$ÐH¹i@¥-M.R67eÎc¹Âes Ù§G\ÆŠ]vã÷{¯™ºr¹•ë¾»¹Za Aæ Œi)#ouQ•NЈ긅4›Ž"FÜÁ‘´~ƒi‘TR5ÚqÇ#N/J·^Ê,‡#0:jÍÌïdÉŠbÊ™ÃÅ»Qå#0–•&`Y)”Hà#/i†ûÒ±`#0¶¨‘”©E4Ôê¨iRŠüZFUÌ­ñ‚¬2ˆêUÑçÑ#bïI‘cdãnLr::H8pÁ(Ì#,Ôœ„Mïs‹ñCªÔ…B·¾ ™3FZe±‹Àå€Ë“5H”§!àbƒhÒÞâ+lV¡J£È¤Ë$cD,¡X"«Ù{Ýö4‹u'·–U¦Q¢qš¨ÇÈÃýžþlF~— êzTÆÓýb'j¼æ&Ý7Èx[e…±xEi—̓¡¥ã³ÃN/UA2‚bõôQ#,šŽ¬­gê‚Qµ G#,Óê,ªÄÊ<|`æÎäñhv-§Ô>öÄ¡%üéÖƃo©«oMFä™L[Ù‚“¯-JÐä<UXN—QpÖ(,žÍêEëwqs4ë¯]? 6Ƹ#/Nük–j6äoj•sKqŒz±µZ$ÂÕ¸d€Ç­Vß=Gè–'¾:—¬ÌÙ¯rðÛ¾‹Ãç^,‡]à‰ v–8Ò“rÉFa¶o1H,X §¾° ïš¨ÎTglD?ïP†ü‹àØÿµš°¥Á±TãÚbÍ×a ùFcZ8ªƒ.!›XÒmÄam®M‡cÊaHá5¨0X‰Ìqغ·w!‡RÙpØ0V âþD¬?{8ן.6ì’yÙäuÕ˱gm™ÃY†-ÏWØ×W¾ãÎ,pjmG”í™ôG4ÈPÜì(2(ääÐi 0a2BC-»âÊÓ¬lÑ}#9ÛW»h 5®úðÔº6•ìmÃì1z•[È<­Uw“* îU—CA~¿*ì;®¹#/Öæñ¹e¿%¶ F ÁæÐÀÛÄ2£iJ-âß8¶MËk]+•øIh5¸Øn…å†æ{Žüí;Ð븣m¨ê:ìž"²o1,•½>çœÞ[;¦ß#/+¥Ô`*Úd1!¦3+X©qÑ~ˈ¬Øí §±ŠnšºÆ#sl)”4±\³X™ƒÄ5K§M˜œã1dášm–!‹Np^r*0ᾎI¾ÑKŒMd®—vÀ°­C4rç9$Iµa9M—™¡ËÃì÷«Æå_nŒVE¿M¾ÉÈÛ •4!ù‹u³Qçò¨ê\àîéöµÞž–š‡>w±4Bˆˆ¨r<DQ0þ3öw¶Ç»#-/Ž¬ì'Ò½fñ‡/1q˜Ñuib•5[+å³êÔdI̼9Ñ$@V#B~†TÞºc3OÀ¦eâÝÿ’Bâj÷m±”ý&qUþ(|Ü®_³¨‹YÄFàªÞn~#0 ·™¹ìY\,†" |6UÛÞ_9µ‘#|íRøÑ8»HR¸³‰BËç9>[<D¹¡;$¤W¦§(­•$x¸7Ì& Q€©ÛÏL™4?¯ž‡7£JéÇo àÞ0c#/{Ç9)—S¯úûD;®ñ_lQÞøS‹EÁA*XêÝ¢ðêؾ†ýWYɧÈBzó!èDêYΘ¬ÛêD?W5_Ôãí¹ 3¼Í)KKÀrtʼnFHÃïµíŒ„Cų8FŽ· “}`Òeºèá4ï§廆%Þßkt© ݽË’'¢tg#/]éõt¯!I&ãò§ml%Ù\aߣŸ¢èˆ°åýªSi ƒà­†Aç$ÊŒ\2™LÍÔÓ§žŒ÷Øœ¦ûÓtÚ¨)#0IS³Á‡#/¦Žn£",UƒiÂÕÇB‰œ+#0:r´„Û=XMν0bˆJDÂc»Jp„Ô‘M¶µ†«`”Æ6uÞÄÿå1Gqøì:Cšoïß–;Gefû;‰#‰¤%VðîïÚCå:¾5ñß’ñœÈÔ$=åc¢Ð¢7zCö±»Êa(œdQ*ÇkŸÄ·ÔgÕWêÀw|ì,Â:œH9dàÁt'o”;ç—Üið  "¥O¼ØmäòzšJì-üIVK‡‡Ücu#j…ŠòÇgn@›%<ž'>n×;Z‰ÈdòýPæ„ îóÖ€'{¢¡…@>ÏRº?£´´®QÛ‹ŽŸè®uÉ°>3âØíù™Ÿ­¾º#/;´¿ÀÆôªºèÍÀƒH¹9œ2Ùh@H~¿Ã…Æn~Pð{ uð g)¤Yàf4‰b¡å9ùx÷°”#Ùï^ñ</íR€`BŒ1,Ä* [K#,à‚}Óºë{éŸMW%·…#04† Ý­ÁðƒÛž ~~–}sì¯;â>ûÑú5d]YÒ}¿ ˆÃ4.¼ ahäÆ#úŠñ«¤¥Mr xó/<DF ¸s0¿Ä´Ô…2~pèÖN¼r‰½ê–#,r8#vèc¥Çå$Gh¹Æ§¾à©kvx7`M1sÖ.%ï–ÙÉ€ý»}.HÒu•s—]ŸèÖ8™8ýEïjïýæè I¼ön¡G^&LÅSå1ºùåÃyg²)IVì–\—¹#0Q´Á*Ãøiv@uá4p‰˜E^tÀ4x#"Û„œV{î>Vv<ŸK>»¼<䘉~ö€žÛVrs< ôŸÈ–+k¬xÍC»UôšB$*dQ+ë æØØSÛ­D÷:n3^Á÷‰ªž[k°/´ƒÈXnèë»ì£%üguI㵊Knî-7§÷ðTzÜ·c¼Ÿ%ž…Õ¼ôÛ–r|Â,‘ðã¤è|F—u}u ÛDÜ Ê$â½á2I©¼ðÍk.ËŽˆ¢MÄö?»£ï:õ–JIéªôAfÅÅ„tCC¸:f‡2cÓÄôã[ëµ›”Ð72^= 7رî¿Am>èë/àOOЉ´êiåÁš,ùÌc†ÈJõZk$ÿ^zePøˆ8£§Z#0u»6nÝ}È¥ñ—I2Ó™†¶>³4¾<†3^ñÇG»!2Ä‚n¢ú–ÕGW'>úÅ®¹‘ÿ§¯À?Ü›ÑCîÉ2ènCà‚Ç ü¢q´ð ü¹Ÿ|2Ó¬7,šã&Î;fÔ¥5¡œ’ïÁ4Ü ¼õ‡>x¤´ZaÐéÃôÔÖƒÁÄqãŸ~[Ý·^š:+Њ!‰mÞ¹¾#/aŒ¶Œ¥3–Ã}-¾…åˆÌµ´.Èòš¢žPä=Ž¿Í¹eòîõ3Æb"€ÌÖ.ÖÏ/Còw݆±ATz, Zk -QûÌü~^X9>mïÞ|ôBá..‰ÄªÄB…×µ|{/]Šzs”ìc”’OÛ—Ãy‚ݧ²èÐæ#/ödñ#/1¹Óå¹e—ñLF~Ø<W ÁƒÓ¾ô68HfÁ1GŠQ t!nL9(!TÈߌ#0®GÓ«qvNN_¡èíá5ðî]¼2ª]/ÏÐMŸ:=™¨üJÿ€ßo^ã×Tº¤¯ÌšÊ_Õ5³^çF]_»Ž:Ø/×#/özF‘‘Væ¿TëZ ¿ü:ôÚj/òô}W•C«Ç§]´kMκ‰¡ÝºÃ“²A¦ DV²4×ïç9ÞæCáüúhÏ”aŽ2]¬®½¬ÚùJ·…пEj$w¾Ù›z÷xâàúƦ°Ô’z¸ªÅ(#ÞŸÒ¥Yåû\/#/_oAsvN1š Iñ¥ž/­¥¾¸É4>XB‰xV®ÞÕ÷º1žnæý3§N†órf©'~ðóvóeMeZ~4[BnªÓWѧú¶ç÷MNîQáqcºL,ŠÄÒ°²2º¥_r ƒ•$³f"EM¬¸Q›}QÇ­cè÷­¼ã¦œãwÆa4‰µÒ‹Óï%ôÖ¼ÞÔm˜É××®%Ï7.ÝüH«´€¨f@øv-ölgMÃÜš<à‰ïæCWðRñ¿OEµÝrŽ¼yÄIÔãaµGˆEŠD•g&wj0q+J^ÉTÈUÌ©)JãtÃF%­@l¹Ur„Š™†vß·Qé6öh÷¦ÖÝ0lyëA(·77žË©ŠðлOéÝÌk>~øô«\‰ÎùòSHq«Ä?›Î®:¿\x²öweÉ#/¼ºjÆZó‰ŒîQë1ïž6«Ýæ)CKfýN÷ÙJ-Ñ)HÚ¨s'9€U#0ÂË_]B5C70lÛl“ìe¥÷ãl.a°S˜ÁL¡ÞW1!Å>È* vQ.C´Ôh¾±v’ç¤#/ˆ?˜zQj…çÌÙ[ÆXÞàÍ‚ˆ©&“Ú¯¬ fÕ9B·BóÍÕd|Ý<í¯FþyÏm°Û Y6Ï#,vljR<ð!\;ó¼Â ï/0ýÙ9<¬±16Y(.ÕéÎ!´ª vI“S+R€Ø‘€Æ¿‡).w÷VèoZŒ}ŽuSg¢‘ÍN'âRøt¶6¡/µͱ’Ñf6ÛfÆÛÔÛ{]í#œK±Ù{mæ˨:MÌ»ºººW¹=ÏRöý†^ˆþŸ´ï}»‰u#¯;LγהµÚaõ™–ˆwD" Õy,ØOãËÏR½±©ëNK'I<"ëΚC–¶OØ<UF)¾•Qá·íÒrZFù"TA8EÛ©fFVÛ›[ãhròÄ(ojuýì³"46º9ûTDñ®"[vkA¥Ýðˆ˜žT2ìØûòXþBö û›G{¯Ý!t–§Ëž#/{XÆk($ÍÉÝ4§AS“˳¡¢îgòùølñšÖtÍ)Ej:xvhï@õÓƒ|¿dØÎ,Ãàvã>\‡‘p-ã6Uã:¼56%vÚg‘\\ ‘[ ÏG¤»K¿éÔ‘Œ!_O ˆ{•H®Š<Í@hªŠ ßøôQÑj*ªGksÇGæ5`÷›%a¯#0§OtáK‹ÕmzØBƒqXº‚R€â£<—Ÿ*;ËŒÎÊ®uƒR“²#/‹¥¾·hª1ÛdamªìñËî¢ Ö]Yv,ªj_À38V"µ×¶b©B6Š^ ž¯bËD²ƒÇ€„1+#S›—…mb—µÎ#/áôiª·ÉX^\%Üõéκæœ'þ¥Iª¬úƒ²ªkðͨ]ãŒ3×Z@æÂÇ`¥ëµØIœ)(©5ç‰vüØÅÃÌÊ3ëÝ\Æ&qÅøRÌ9ÚgÇ^±72«rn’âçápV8ûþzòçoïï'ƒsÜ;¾_Ž>¼ùÜÌöñ¯=0ÁÄÉ9›_uÒ¾3ÌùÙ#0Ÿ$j×U˜î[îqx±u:ª†º’ÐU†HëÄCË1¯O3íŠïÝp¢]¾±Âå86ÄÛ[ËW¤«Þc:T‡r¢Šì“;¼g²IH21!¼†®#/k³áœfvO«Êé¾,ÛYæ%Î-‰q)]L4Ãî—Œb/Û·Îgá4Œ꟎³ß;sŸ© ûè’™Iñw9ëåJ§ †•Q³ï§¼DHhãTêE+eY–Yí+€rÏý!ö*WGê¤÷ƒÉ õ4ÅF–¬ì5‘–ÖÂê¢*uóØ$ :ÐÙ>O‚´<¸3¹ßMýúã…S“… ŒÎ d­Õmâ"Œ"[«¬¾-’Á–Aœ*Xªé‹Ü.ÒÍš¡ùmsæôTñ¨ðj?¨÷¹!uƒiÊÆÇ6vÝR—¾8³u›¤ÅoqŸX÷f¡þÔºëâ®.ûð•Oº%1"]Ükœp³h R팄þ«KXL×"Šºn iø<”RzóôxݘmòýY<Ô¥Õê B7³Aµß›%…_é>«3‹NŽÏ<y½üj-ã½ï"Y¯žÏ4.3úê¼½üÙÍ?)k™—…à Ž Š›ÁÕu'ÓyÎÎÙû裙ÌË‚dŒáÉ[ù>¸éé×{,çO»ª^ð-š)AP`Áý’£²6”n¢ƒ¼€í†‘rénÒët·Ä1šÎç=ényÃN°'G ŒhVø”Â\bm0ÆGuÍç™ÊÝ·r®½èÔü æÿ*"}¾»UFü<JÂd^˶Ö{„¸XÁÀUÁL\ÂR5Ì2É°q2£>¼'É×4ll \íÒèYG#©ÑñàÀV¹‹#0Ý“š£»×ËÎóÎjÉ–}'UðÛXí¼É+í´®É.{£GA«±Î"²÷#0e š¦Ñ›\r†=2yto•93šqÄÔâ¬y(r`ü|·¿èŒi>Ñè¶^®u\úæö³<;Nzûo<ƾCÃôí±{9ÄÌ5³¼Ò‡ÔjÆ.~‚’!F÷¯…UÅö¼)4×ÅÏ¡‹7k²y7yŠ„;mšhÍš±P”Ä–:4GV³J¥]·]Ó¢9[ ƒ„‰8Φp“ LÚKrº«¼7¾TiJŒ#0,ƒ °$@#/KÜt@ ã3¨;ÈëÍ]Ô|/F£bŒNÓe¹é ¿d#Zµz7n–5‹Ã#0UHVñdZ*¸({?ÌMÕö k”¬B’:HßP²¾º[:ip>R%¢Õ0éÛJ刾U0¾µ¬ïµi.p5Žë»}ôu‚À3ÖÎYâ$•ÜèNŽ1Ùv-°f›–ø٦ʳ8cû®Åóè‘økTêñ‡Š÷/–÷º”b_ÈP6FoŒ3³2b϶=4ܸA‘‘oZXJR"H‘‚„ ß‘euyDte¼º• #0E„‘¼¿-°»T£áÛi/aöVðEìD^{ûçž0qò¯[®gÓ¥êï•,ljAÞžFzƒ8»SRnå‹][#/UODàî7†vŠ 7©Ô­¸Yã¦Sj]-Ö¼­xs²#/ úð\lLN¸B‹³®øadžªõÐ:¶ ‡So2Ì6MP+žçs#//p»âá8ZÑÆÏ^x&~Úƒ!?¡JÆ!C¦+jØ jÒ4ÆY¢Ë,(á²`;ZàcERs fàYðƆ LBôb¢ çªøκïg8ÚG»AÏêÂ×/G¤µ‹ý+š~*&\\—*›DU¦uÆl' üáóð›Ò®Kl:(NÙsö½RGÞ.ÅÛ¬òsíëx[æŽÿ–­!$)=yóÞ¦¸1·êÚ±ð½þ2@Ña—qÝ«Þù–w¹RîÕ³¶®¹Õ/Úô° ÑX:N¡²ƒ~¤77/Îíéœao¶õX›>4ê,CêÒ¶X½|’õb9áuk}Û\çWodAMÊœ¬È™î¨Äû½c‰{Æe8SµLg˜"du]\pZ»Ä—\j‹õêOÕ‹7ï…¼ÛÞÚÑ>þÆw·ìÒ¸¦‚MRË$ˆ¾#,XõªS4µµ+|cJöŒ %!]#‡{#0UâW4êv•ZÔJ`IpÄÜõ|åuJ™¢D,*,Cq<w¼pœ'CëIŽ]‹úñi½Ës "R«xw]”Xz–ð§¾)tóó³?›Ï™ë¼œíÛTˆæT°f·ƒÃ©ÍHòÛ#/*¶!3ƒŽ‰…ƒ…ÑTŸŠ¶tâ¹é{¹bë2àã$.DÊ÷#/ ©FŠ­p<ZÍ\#¢Œe#ÐÉçtÊP^¾Úyn¨ö[ÛlcB^Ïï7ìÕ&vÔ4¥¨Ù¶¿ÝXêÒ9m"Ôi¥a5ˆ«;¶<-eÏ…'ŒµµU¬$ÐÝÇm[Â*L¥J9-‚ò?ÓÒÓ0Ãõ¯ÚDD¤1íWž¨ktÊ(úW—²PŽ–椼L—iÁѬ®Ã#0[Z®˜ôGyåF+?EŠ=«Ï»,9~µ'_lºÃ¡×nñÑŒìaû˜ù²‹š¥E¼TÖ#0Šv”¸ð7J®W`@p(€@:œþ~¾®HI7öIFÇï½ÍZy‹™¶-\í«™ÎåŸl±·x9«Í6‹ê-¦?A!Ù| Ø=3Í!œ#,ªa%#/‚ÙvD[RªÅð ¤õ•®¤[uìo¯YÏþï=¼—– (!ß*jpM…(Ùibù+NSá%ÏÃ?˜Õ!vð*ŸËàK¯½ÑõÏõk«tæÓeMÔù`ð¡i %ZŒòææ¶ò•XY1Ϫ •»nû˜#/Ç- Øõ«Âœkpª“Mà—Ì+¯©r½½ò×RÌÒg˜=ónfbÒ$˺ÁD¶Éì°Ébh/þßn§|ÏVs¿›¹ðC­z§jñÒ{ób€ëÞ*'{3¾{MîåÄÄÑUIsdÃúáÏlôͱö!ù¨é7ÐY«dg1Ù6q’í*âÖTsYEr½ë[ê “r¼£@×îVœ˜5a}[¼váê„Å<½T`0:\ W/]²x(^§GhìôûÏõ×Ê nyò‹Ïj= ÚO‡wUÓ=n;Ë”{ëåâè½jy›/žÈ¿ÇöÓš_#/øç¶å®»íu][EyKpWjïŸÀ^ó{ûÊÎqQ2:='ºœþ~y:(Ùî:ÌKª ³’–p\`„¿b PÁ@UU!O6Ñ£gê*Óu'@ S‚³hô;ûÎyA"P¡=[ÎÓ¸r<ññt”EJûœä‘ì`½0â+ ¿EŽ•”¾Hžõ‚VP#,þE¯55ieBU¢sñçÏÕ#0¹Ý £¤ZÕP’Ú×–^&ÒË“®îtuˆ«úZ£=pí°=¬.èuNñYw0=QìŽaZ9…*„À¡¤šêŸ6m1¥êc\EAãÚ”Ëâß#0Èu¡P*ÚqÈ;\/›¦‰.2ÙtF[¨>ž¯ oj’‚àÈ*(,.r'·´£ ‹j…žCi%qòJx‚ê#§_8²¯¿äñ—wÈ2«ñøÖ‘¨I|Š.÷Ëź|”|¹ïÚ\¸iöøTÅ5päÏ]_Eßs*üë,²£p¢Äæjì! ‡º ©ðæ„òYȧ1ÌÅÍiå]q7¦_8Þ¤ÙM9G‡bö‘´X‡##ÅÊêÄÔÍ.®¨øYpžlV„FÂ0ÁÏ.#,‚¥“NáDö ²tw¥uÛQ3¯æ¡ÿ ½x¸;N;ÏÇ}Cd—û6øãËpüpM›ø©ñœÎHËÚ0Í(»œ˜Áµ7MñS©÷*;YîƒÎ1üºÒ;;¿h±,JaÝG°16»h¢¤b/b­Xªrl›C è%ž›ø^'¦Î¥Ž´´*;N¹õÁ垯õƒªšg=bxØ¢—¨Ý#8ž*ž3}ž®„±Í`5Ñ6Ä&]»*Þƒû#/Á°k:‘ϱ߾ïò$³®®náà#0±Ú øúM׿¶b¶ˆ.Y» _<3[V«`4˜CËvBf7ÂUîзÂ^OYæx¦.tÖjz¾Ú}sm+„êq£U΢§/’5OV°ìð‚StTŠŸ×‹o7RDþpøû|pÄ<箺‚&Þ\ör ׈\ZŸ¿O'j²"-‹[o†ÂkõôüŽ5{8çÅGœÒá²ctZ†™Ù±Ã×Ö»«©ã0±LŠ…"ˆy›t ){k3Ÿk ®†s¹Fäf|'7š””·Œóó »Ä:pt:8â}žøò71ξVâ;äx·:(åýQBJR~¯ð¶ž-zMªÙˆòò5te‰àÑ~Êô­ûÃ2óòöÒïÓl²:îÐç-Ô\Œø¤‹a¶C4æБÛp8sLîOe…i¿_koQXçL§ØÌ&‘¡œ®î¨âC¨ð¦”´ÅuéŒÝ|jw$éösK+#dæºwÜ“V¾ìcUãúãËv: ·;Ä._îqÏS¡¨E§p§†•ÞÚØ)iáxôc5^D7ªlKg8§ò «rU»‚™“RÍ'¢ãÃðÞ°Ü@°xWºNLšN¾­ÖXA|yÏ¿†o;Þ~ï#¬ Zg ¤c·IpôjϼðøWŸ#05ÆŠYmA`;mvÊ%×êæVùÑ{^ Ä£ ÛüµÙVžf@ögëÍZ õÒº¹ˆ(:M÷Z)—0VÏ‹¬#?·¹òؽgV€ðA4‚¾ªçcôMï€ ;¡W™TíT戊á­îÕä¶#Fd÷ÅÐîôpÍ·#,M¬¬)pn’ä!ð¨ôí÷Úƒ×é]ËS<kµaê©l[zŒyÖÞˆ1Î¥ÞÍ)ÞÏN¢˜tâ'T#0A|6$p¦VëÞsGïþ5±ô*8sÅi@ÔžÈfqšõSú'ìvÄÌÍùû=þø3ùEùÓ<ŒËv½“’~­Ìû‰-å#0æÅ’°¢²7±(ëÜ2›¯ #0*!|-l·.)V-*$štuaÑ‹õBšžÈ„-»K #0<¬ÕcZ€FNrÄ,½>få_ Ó‰ÑÏrùöÁº#,¸®?–ê¿ïwÄú¸tþSá)g^®<#0#,)òž’?DürùÁïèF|ji³€ûå(I«£Ã´” #0=!ð~Nî.›‰2ÏH^i‚‰úú}ÑŽeƒåçxÛ+auߊSãþò“XD…TBHMheŠ‰Hµ&Ù6(Ûh( Ìõæ,F$K@BˆNtäyüÞ4q‰µÅ-¿ ¹\ËžÁ>øæÃàLvr7+ú,mšŠÂ–’¯¥U¯Ú–³Puöêhü«#¤ÂtÉû~Ÿ#åNÕ|[=Z´~ôófGö¦6£ºJܘNÿ±²þ-ò†(´cçìòíî ‰‹'Ãïïci"_E£vL„Ë:Ú)TmWe_ŸÙ?”U0U)#0%2Ín?©ñþ­M§Ž-M~š·íR+c»‡éÍíðÃôó×O›ºéZ™­£Âí÷ŒÃꃲ±mtÈü\cƒ,r1#0#0‹ÃjèÚ9pÔu]6T>0mÜŒåà5ŸÕ…Ÿ‡ƒ'Ùò»S¡ZžuÜL3d5Âz#,jIvs%5.¹N¨88¥ª #0×Á„aðÁ\‘ùƒÌŽâ;Ç¡éBtˆ‚r6¡ ÷rŸåôã“‚#/³>ùç¬ÐþêˆbƒõK¤šõp²ßãpÁGÏé|S#0XSæŸ%£x‚Xv-êUY–ñDMC®Ûó/קàûÉW3Áq*ЃtÖ•‘¦U2;£c•#,?Cc”y6EÇŽyl üa­@pzo5‹.ÔG2ÖT›a~Ÿô;Äè‘éuÒ–!ÅujgÇ&ûu'R˜D J8QME‘L½Ôîã(‡ß=ÑÈ^ÖÞû#¤w|ÿ®Û#PPé3¡èˆ©èÖ„ùè`ie+YĽY*3ÜȦX ã¥aÓ5§Å”¹ÔÅÉ2$ 䚔 „ûΑ(º´£¸Ž–Ñ,¸ºù¨G×Ƭoé糿i‹m®w¸ŸÏ#,¨vD8Ïü¸XÓ¡ëÀhOSM~g勞_šÃß|Á²qñ›a#ê“o?5 áâ >öšk¸†í´31„Lk‰{ç![K–{aá¥ÀøÏ>°îØÍ~ŽI·éÓ»b9ñìÚf¤QYUÔoj¢Ê R/4 ~îÏ0lÈ I5*ø×\ÊÌi8†–å[øröŸQ1¼ïùï ݦ2K§H 9gÒÜJØcßVtp>-ÂM2Ðø_ŠýYÚÐ/–ïÕGg,÷˜€í\œzéÆñ5{ÕÏy œQ‹/EÌ@\ÛÜžK€ˆo)ÜCœtÿ%в…㶥fê‰ì _tOQR”#0ŒØŠ›énjïfQF™Ïˆevž|#åYÙ #,}S#,`Òã·º,IñƒŠ´u[2q8ƒ;ñöÌ¡¬Ö åîM ò˜´ 6µ©ò”CHàj‚Ql|AKhiƒæ%µÎ 5¶›ÊAh! ! NQøŠ¤ëKã¡x¹wœì(#0 „:Wk¾¼âpŽŠ7\_›f­M˜¢¼¦¢øp±Ä3 r‰¡,F³¢ [ÿ-º ýÿ›×cËb:í”Vxs;ò€^#”“·nè³Þîj80–ç%ÏW¿ ‚E±çôÐa3ÇòI)¹ã<`!h,vižH(PB Çbîœf)Î×àЀŠ©v»_A.qw.~¥¸°i6”Ó4Çé|´±8^ï;á8ðÁ'Á…|æ˜ñ1è³Fqv`‹QˆÄ$%d<… ©TœÊPZd~Q„ |€yì³U¼ÙÒ™ùUeÏWµ½ÇŒ§A]„!5<øk¬ƒ¾¨K!¶9bvÝ_ªÁG Q3V‚Û›TÀa€("2#0ë z¡P¯ƒÆ#/»2Äð€8"ùLŠirÕ žÍÑ ¸Z«\jÌ_2+Ú·‹³xňÀ“‡.€ð”ß6â®Vã–aeaÇÆ;eƒ€¢"d¡HÐŽ{€›É¶ š8ºKd„«S•3È]yXºmþnžñ›#,Fq“œÃÁ”5Öìp¸Ï@Î0›ÒAÙdùe|dsHjðû7ᾯ6Cuféá^Âî¶BÐÐ F¢C’ló-ùŠ¬M&X¤Óì”@¥m0žPÔ+ncXÃt‡Ôîa kSŸew| Ù"i’ ÌxJ2$FßE:G–\Ý1 ˆ©ê™›ö™owÑs,#Ë–ôœDt%§žD!Ì·ŸÇ~˜/+d§¦ú^Êö›Fá¾cѺ†#ßÀÜ “#/¶# Å€*,yy¡#, âBE”wåkƒƒòéµËª#ªíÊ5^Èô'r£Uö.ÆXŽDÚæc3ø£}Œöv×¾r/¿uÆs&u€•3²@@à‚#,£¤Úñ+²T‰‚Ój­wæ~2Ã<ó*zp×#/9ÚÖØ¿jýöQÃ*——!û³¹óü×¥û;¦,ô¯ÃËr.-­–?­2ïqP‰#/ÕÉ— Zlœ¢è ûù" x°ÛU‰b„T€C@MÀ(MOmø2¶s’ˆ3Þsº³ì®m·ìð~ Øõädœàéöðâ G%ÄZ4“j®A­²Øò‹˜Ct`ÎUë(3ç¦í¸¾iòÑe‰¸¤¦ÑÒþdWw,/òmì¥{9€Nãô‡5ÇŽ/m\¿ÒžÎÆDÅû® ¦#0—uù\ÈÚ«H6D&&¤”#!C;Ó§L@ÕÂeç5dž:}æÌuÕ gÓȸ?TªÆ¥@êÅ't¨ýÀ‡29« ™ŠŒ0vp汚…êßH#ÏkˆýG#,Ú˜Žï€‡îA ¨Ž‰îæòÇôãåöùa¥#oRßõád£­UÐ^­û1HäêLz!†Ò'­Á‘ZS@x¿!¿…wžÍ‹‡&‡·ºº<„ò'/_˜àÕ]sƱ’×v]NdTß#,Eä¹Xk&¥—ß]¶Dôöô|¥5‘±_ƒÃ"íýÙÛÒ²{‡ʤ—¤¿Øáì’ Ã^p?£·døê|ÍHÖÓí]'Ò÷Ã[Gß/y¸Žîrè|˸×嘼Züäm(ðÒ¦§^„¡—€šQùÄêÞ¸D®‰Æç'º©oñøâx>›dçL|²ŸjkªôL‰Lùëå=ž ê®G¡÷Œþ7­>m;¶«ás‹éžLÒ²^n7æ³½¿­AƬí{˜€Ä%ì¶xafsŸ£¯m·6ù4vbòÔ U¥Þm…m×Eqü¹,7Â-*P‰}ÚîŸ)’[qóåÙ·¯~šõ‚Ø+¸H¥O£ ÊkµÚ!@’ÞmËïÈÔ÷&3± 2¹]+aê8܉¦(·NeŽ/q¿×g.ÞQ{.) *ï˜D<ðY£I”L¢ªÚ6êä`#/ê+y1Û¨¡¾5z””1Šš´¢ÒE¦Å–"ÌcÍ0± ‰K«°T3Ñ#0#,2°ñ>@ãlpMh&IÊ ãi¤;;½½ϯHlÏO˜¨÷¼B-p&í(}/’‡‰bSö%?ºB¸’Sr1ÁNžÈ!=#0L¼¹>µZJz?Ô RSN?<OoMtˆš aþ¶[þúS-¥E˜ÚSk²åù“·jóÏ+ÌÄå#,¤’Pßö~íäfR¥4ð¢/¿9²0Y=·n†Cùà‡ù{AŠ#Wê÷úëG>º ý€ þq}+ñ'G—ÍŒáÐ}~Ï0aüàøI‡«O^DX¨ª° ¤ÿP´€Äÿž ¸?®@ÿ#÷þ÷ÍÀ7ýÆoäû¾Ë`yêµ<Óž~lÌ#,Éíé¤MpÆ!{Öâ9zÃ_¯ÛÛå5èÖ÷¯.:`)œÎíÚ›!ˆ~Fᵞ)ð_Z0$ÙU')ÐBÛ“jÙ,ü8z\˜èñÍÈwú¶XŸEz‰Â÷úú¥ËøU0¤ÞÄñé¾d¿#ßÝ`ÖÔŹ°„‰ ¥³ƒO21§N=½ÊŠ`7êÙª”,ÛÄ˜Ç Ç«{Ï“ôËž¦¼fOQó#,‡ #/¼ÃÍÕw1ïÉ™ËÒ{»­gq¸^¥ÏS 84—óv`Uwæ¡ ŽÜ"(€~#t„‡í̾q£YEõ÷þ[îUoð©AÁ‹6'Ñûl:r¨jøê7fRKfQ ìˆd_´v½7vCeãdò·£>§øÍ V€‰è)…à;qÉ?¯"³ñéü w»º¥L0[L?½4ÝþèoÚ„8Dð:— ¡è@¬J.‚È"7@Ì\gbaÉ(…˜zب˜Á¨Ž!}[8äYE‘ s-)²6R87#,¾€\À 0›)`?Š9ùå"Ü2M…’ÆD‘n—)nPY#LÊêU çºÂü\°RF{EÜ.v2+ „°#aÁ"P©†#0E„L •1CSẠ¨!œUþT([g°fCÊIóö*ŸïݧÅ}þ„>uAf<#,ÎABÀ—DÄó÷ËÏ›/ß½^?´'e_ ×ø¿±pŸYûš5<%Š–½UtZ#/à…Ñ0ïŸAø®ûŸÌþÖ§­˜Pž–˜È̆ËI31ÉÅÔ Ó´ˆú¨–+ç˜ïI®óûÝS؃Åÿ4›g:ËNšàãÃ9#,$Øn”‚÷Y<tÔ닼ÕúÌ TÁ{P ŒAb”ÉDª±‹tɶqpˆ›Í=urjRL(»kGeN÷»¿&¾#ßß®QŤ¶’çø*g„+–!þ6˜Xø5i²WbYÅGæ¬L}WiFÓIô §Dà¸Ù;<Æ c#R«.^æ]T7dðfŽªVPô¿áxyÖ‹L_-)ì´=àv´Ùæ·.Ø[ç“°³Èf¦—OGP´¤{àíۇ̇{=Šy”´ÐúÍ7Ÿ×5SX‡uŒ¸ÛKâ#/‡Ý`Ç&…ïìT®¯1$†'çƒÈÇ×¥ž.¬°½âщEAbÍE´i4Ùf´™˜A{ÛêzN¹9*ª‘¤”UBÎÝ~j#/ÚOƒrxF¸8†sÞÛqÉp #/ÂY—²·‹º ‰7Š^ùAJ‚Á’u$3Á’@„¢~c±_l\D¶ß†i3ãæÄýzPÆ=ç]¬l%;È0‚ŠTMº›ª6¸™s¥Ï@À9»á\7*îµZT7PÃ’¤†”èe†DòÚ_1rÙÍÐzÌØŸ‹H˜´ ëÎðP>á„ &Ø# És:P¸vVòl ;(ÆΆÊ8jQ°RÑSoA×ç¶Ìcì†XUäûzLØ©»YÄ7ÂK™c[ÈÓ³‰Â\#,ÊDô¼a!äyl‚`Âf 7ÓØŽÇuã‘œÛÂÇ©`b‰ÞVzhDÔ¦Šw'š=î–Dã#,çŒhŠ¥(Ú*˜Ä¨‹%kÒËi*ØÕ¨Û&¶ñ€Aˆ ,'¹Ù3^ði1ÒPE¦¥ã\Lˆ.Ú‡!.nÍžu„ž´”-‡ªšÜq"#,ÒÑãtÈt}Ø»÷$ßž#Á‘Ka’\ÕècÄáÉÙÖñ$Øè‘bÔóîMÌ|ðä…ëÛj'9(é³R†öãÕI¸Ç¡õuXgwKI ²m±²jÅ_©­õWÕ%ÃÁ qð/;ä¦já”óñÐ2lx7O#Ó°"¯xÖE("ÅÎî4 zÐr® nµ%,¤É$Œ·ž*˜RZøT)2Nyl[ý•¬Dû+ï±AyBdvfÆ1,˜H~ØCDÊ#,ЩˆÔA Š$ˆBlmÊÛê¶Þ2ù-«æ](HU ÒÁ9[@š‰ìÖ‡ž>68<xÐ#0ˆ´…QZíÙˆDbÂɵOŒlDçËlHFGœ5 ë‡ Mùó·¡½¸rF1"‡ŠŒ¡6Hjƒe&÷òW]¤RYVºˆçÔšY.SK€¢¶»‰lå o‘#0$àÔ:Éa("ç @Z<ÞZß–Þ9‹Øtr#/À ¶ñŒI#Àhõm©BòF;ya’bæ¬Tì×+Ã5”°ãD(‘H¦’4 ïçGJ³§°×SJ„ŠÈ75D#wG,~»<;1Ö±,= b˜<A4S¿u'˜ëÛ› ˆ÷î{[ì¿CÁÜãë(ú, Ê"¦ê¯xûTIö«©ÏÇ{ï™Õ]¹]K„÷¼›3UÝÒ‰h.…Ìg‰B€4ïš‹ç iO“œ' rÛÛ15§>á,KÜ{†“·Çf/cïk…"tÒËÍ­:Ô(ƒàE(#ºZÓ:ä†0¯U+D@„ ™ ²ÈpXd]ä#0¡ç·Íˆf‡3ŽÄj¨#0”O_§-]ûÏ5Ì–æ{B¡ka³~O§@ãß”Òð–sVI$˜NÀl˲! ²+)½Cü¿9°®½–«ÇÍ¥Ž2ìwbIêöwr{™N©Mn¶nOyGáåÝŒž4l„`¹‘°1šQ/]Ô‘ðïlwÌz^¾Ý÷u±§sá¼4=¼Bá÷D¨ûºÕÓ­Bá“…“·ÍÎó$œ>¦üNU<¦Ô$À,&ù4>¨gg£ž}Ož`æÃ;¶è@=.¼Lû·lï;+ÑÆõË£N8Ümàü€¯Fë5ŠšiF…&åø|ìÀX¿t¦7{Øê>°EâãéÔí³kÖÏ.šeVî83ÃÀ¥#ûfö†Â?ov&mt†wÙ‡nŠ¢á;¤%óï>;Ó lUŽD¬AXASêb@HH+›û#/<((ÙÉŒ½ºí!é`yÇ2ó î#/~Hû3ÌÃ-}¹NÚ6×nGæcPÃn¥°÷©ç“4V-ÒŠ2Nêâ Êf¬ç/‡‘f0Z ‘P›Ñ)PAÅ,˜&™ï‚ß÷-:­xÁÙ=héÔ³sÙ]ÉÂ|vÉÕ]¶ÍKéZökjlÑf½ÌûŸ7¾âÆÌ;%]ɼè}†žV½xLó" 7¤342<.ÂLSkiu>d#0ŠÊ§h;q}tÙ¤¢y°ÎÙMÝ,‹z=>/õ¸>’otÖ¹NZU@ŽÏ̤¾±^2û1ä¡Kg«}“èDŽǸ n³`ÏpjȤF0W!‰ÄéK7$Ù+¶Š‰ÂßqvŠž­u‹ jM•¤ãAËÜp7Qrs‚fšc½¾ÿõYÙ‘\·IT0í£¯R¡”0D=‰vCd÷màchÌåTªV4`rúÔM¸…˜`A<Ê#/ѯ‚@‡…èbÁ¦ëÓiË›èM²;¾û#/%9×C;Æ ¬íØÓàž„:&Úm²@îiÒ.¯íü I3`n836ì­$:'bá|âšë›;8{ï ^|œˆkÌ3“ÔŠÏ8Tv–´Šj¼†o¶g`3DvåÌ6UŽÃ.5ÕÖRã1ÈÔ†€ï"< õðºí#08Ç)P”G ƒcX›§SÉŒµ“ÊÐʶiW½ö\sŠjD4Ž¨B^pr,æ@!¾në¾ÖÞ-#14ZOZê¤ÒdÄͽµôµëWè/±×ËÆÅJZ¡q“\XšwÔÜöËRðq*\‚I$ ºqÃhâŇljô—£ð†£õXÛŽûÔ÷ì*5]6½ÙÞ|ê„ê¥ÑdEÔx”SáSÁd¢OJQ³ßUöŽ47a^8;I‰šO¡'49úŸ}tÁ¦zx¸@cßç± 37kÊî°hÄ•sŒà´¥ýŸ,!k“8%Ð;Ãç^£¢ÇGùÙ¦øTŸœ†êÙ@q›IAÃÙŸ_-{5Ã1uÙBþº &ÁåÓÇøô#/žÚ:ì]ÎDÀ}ЬJÉóÞ‡T•¤+Bé]u{*½ä³ÃôþLÃ#'Ñb|÷ìûjx€&8€&×®Ëá ¢—4[—ˆuß<B-ÉÀú4køE€ñõ“ô#,"âåTó0ó⟞’¡ÌÃ'Ó~φTwó;EsÎ;á,~‘”•ÝÿøâX¶³OÄ[~ßs#,ÅDßTÊ&{æÏ)±& Àú:¡ËòWÛü%ëî0˜aý³:@$“ù¿ÂN ²‘"€eËHZg‚ü_Fµ©ƒ¥I‚g"¼ôæs`Å’Ò=‚ò•ýƒ]]‰ NîÜ~¹‘Ñò‡ëo™ôôržå«,û³Óu¹E/ò?˜Ÿ¾Wo¶õ^Ö¸‚¯…uCÐDOÓðÿ7£ÃóþéíˆRïÖ$/ûÁŒéNUËIbû¨åZ¤çÂ**ŸÚ‚µ-es¥ç©=#‡j9ìcGþÐAE0íî2;´Ï F#,$o£NÖËVdŠ­U ÛW×4³ˆ3—ïhÖq§Õ󋽡JZGœä÷þÏÍ·Wå¬5BÜÿoߌçû9; ¥™ 6„ÍÏËããàž5Úy#/õ¬Þso÷°ä“ú’’"DCö4ÀÅ Ýü#/ˆÜ¡i)}Uàc þbñ³ðÚð}‘#/HçT?è²~­÷ü?@®‹ÉtÓ#0¨Û”ÃuÀÄ„{Ô YD‹eE]Ù­mÂw¸™æŒ.uBsºëC<PBÊç  L¹Dî=X\à€­$©åG9îÇE–g‘ö;ý-ÉÍÿ[‹Ôönö¤Åµ õtËgðs©+@7õcVïÃâ#,›sßðt#,Hg¾„2¢•‰ç>»X06M”Ÿ'ÚÒÅ(¬ìßõZ{qøÝy|iFÚ§×3ÆèÄÓÑ4ˆúœÏ®zcBzHjëE#Y}ÒÏ—»ãÇ躞tGàU)n1ïå÷I×¢ Ÿ&˜0攲,ˆBF S"Pä/ðå´:?ÛÜwøËï8y¯ê´ËÞ_º»/Ó¥_æuY Iú¾/E‡º'„þãnZ˜ÿ”?Þgµ‡Ñ'ø“[¥ŒDH±d?ÊrãÏá§ß}#0ªæ-]Š*¢%$âA‰Ç>L*ûÄäág—§ [ò#/6üü6ü?´ÏÏ›Gˆ½=™úçZ‘uT<§ÏG§Ømá˜qñxG£¹Âεë/#,RY—p#, ¶ ÿA¤›_'áIÊAåîߌØÝ>s¿ù «‚˜¶#0¦J¼fµQœ¯äª‘{”€Jy¸6®n#,@š¹;l)cÔˆ…s­æ#0;(³îû>€äLzIëì@ÀI!’Œ.Þ;»»óäÌÀó!LȌNu£¡\ÁÝy¹oÓËCʼnîå)­ËßM]rˆçÔŽmc,ÃÄ#,jÀ§slÉ^ß:‡õÖ?%ùJ9GìÿßÁèïÛX‡©ÍéÛºžYŸáœöW,2~9ñÜ]ýeµ™\1Š6P9§¼øöxþlÄNúbÃ?„ÂVE9y{tp…ÄŽ<íºhø4È• #,<ueE•T|¡£Óp~]CK⃇> ´xÊ'“Ä•š"ˆ|Ù±–¦ñ6—hÔˆˆ3¢"6“hÆ¡¼€ƒ9Âj#,çÔƒûŠ?Ë/‹Ë§VyX©<|T#/z·ZÝÛÌ®¯u†{?—•êííFvÉZQDMÛ„!ðýVrÌœ¶r%ŽQ#,fÀ³#, dÌ@z±§‡¯áá ÁÔ%».W ÔžÉ2%©ìP'¥EY²#Hg³œÊ¹Ä ®tÎë'¥$ªÆî² {ó• …;ˆNö ØÌâÅ¿6˱éßKá塼á}îÆ̺-ÝL#,1÷ÏX¹˜„±¹¿ßw÷í%˸Sœýó‚Îa1#/xÔ‰éQzÇœJ††•3•™¿@›<wû<Œû/¡*R¢,‚Õ!ADB!#/ì@£<öþ½æëì-Ë'#, +°ƒ¹o@œ‚.¡âð® ž¯ 5Ñâø“3{J0›}Iø‰ÆýçÙøÏ>8ŸŠî™½9{‡§å2¨€¾´݆ Àx Üšo5œK[‹ØlnÊ 'JEAöîÆÕ²Ú9•UÄÒ¹“œEWÃ&ß½²WñÜÖ·çlDˆ>6G6⦇™¸{?I5)üWÇâ5t¼?÷ó£öuòÏ溮À#,@ú€9m>']£SÔo6ÇñÕÙAß·Uˆ€L”ÃÍíP#,r9úGù©œt[uç#•[4éŠ Ž¬¾R#0rŠsÙV“ð(›u#,¡ÛsÇ tÝ”A#,A¿‡ÒR…»þ9¾#,ÃÃK# z3z¤>Ž?l(@,ã÷I=N›Ù"„-Îûkú[ Êw-6«¼· À<Õ<òôÁÕ÷}]ÑÜÆX3ˆO#ÐÏMo\âpµdŽ©3á–xHêªül“ðM•sî½eôóÖ*GòæzIB]}öÖOKøÁBà·˜9N©ßZí—çC˜1ó°‹B›š8Nlp#/ðºÉÏØé3‰fÍ%@#/¯Ûqñ‰I|öxH”‘äŽ+W‡Š?Óϲ®×{x{UÖÓy®ø± ¼äq$B?o™:k[˜o?±ñý2¦0Ý%ü–Äq¶1UTÄÝý-ÅÑǪxz½Û¯ïÁR©.?¸£­¶µíO3¢9_Ëóû0R(æ"1ñŠŽŽNì}LguÛ올×Ãâxwd]pÿŒNsñ1SYó­FWÆtuN¯j'N|ãmIþå '~ê˜mQp¸æš>вÛk(tÁ‰@½¾„|¯°D{6y«®–MØ*ù¼4ÁïÝ1!%rk`#s=¦/{ŠSY—C¾H¨ç‚Ð ¡îwoR3o=@äGÜh)<K#+Ú£ó¹lÌ°‹Z3Éï¼q¨ƒßeN–!þ#0`JÕKé3‹w©Ÿ×ª¯ÜºUN/ÔˆGºáØ¿„û±V;Ô÷Æhòçh;[Âß&ó¯]æð[FL1Z²mï(ÿg2¾:ï6د›çàq¨+ŠÓo.(å:mÅÔãÏ{ª·N9ü"eŸ¿b¯uu“ÕÒªŠ'¦ÄcÅTÉø?$æ±™‹•´âè»Åœó¬ôμV§TÐcÚߧ&"çñŠwpÓv3‰âû_Xß_Ã,XeNbÑ#¥Ñ#/¦õ¿#/|:l*îö7æmX½Ø¢ÑwºžžëçÍ3Td7!1¿­>šü6(?—ÝÔç„þ:ôv¸g!K߃1²Zôxç4zç–âü4,#,U#¥¹íXˆ3®¿=jꮞÊVžAÀlæX¦¹À®hnó…Úò¢}£ì;m¹§ĺ©?tì¢÷ýðÓTf$‡K¹Ø‹ëP(ïÜÖ©;ùë+Èãn Þ{mke<þ"êjtœSPô_¥ÎrÙwx4§{çé'ÛlëöÂÅ›7çÇÎý\J"æeðþoŸŠ&:¬âƒÜCÃ1#06õ´Û®7Þ¶Î;LäqºS®\"‡8SSÌ“ÆÇxÛ(2ïðwç#/?ŒÜäÅáÝ8éwwB¬whðk}fÜÁ„äø?|jÎW¶ÜfŽÍ#0´¨5sÐïü6¼™·¹8mÝù¿ËoᘇœeãšËkVräAõx*_ô¦<ŠíÌR‡´¡Å³>1ÂU!dzòÓH”BXG®-#,¯Ã3ø©`Þo°Vt>Î#0”¶ØH#/2ƒb'#09ù÷k¡Å.ÙJ8ð¼oŒèæ2Î]гڇ4Ý#/•¼Žw=ËÑ\ŠŠ ×Ç;l¥%C2I}JàHe<¿Ñ Âd¤]‘R£ž•Èá™×}Š²þQÜšÎߺ$·•<<ÊLé茾ÁMT¥Ø•3&^åÎêÓÓÃF—Éèþ˜¶>É༟­Â‘úÄ\Çß«œa½cY–1’¶Nìçú{P_m|%·û/Ê6b\û¥ýmûb»ÎN)ÊÀ=nŽ ¯+k·åo2­j%B¯h/X ¿!ÛþdHƒòM’;S û”ñ³zC@#íòôò¥_¼’-¼©í8>̘ð`»j ÓÕ¶Q£[£Ò÷H ¾ªÚãG̯Ë÷áw'èèÛ¦œ,êŘmüzD;ÔfO+0.ü>²…ïÏÔ~“éÇa”èV¥Œ*Êú†¾5<™yü/WæVe_ŠWv,ð7×ClšYwÛw¼2ù&?uÐ. dÇÙé[|ß #0H6œÔËN\«Olåh‘Ö¾OoÆ,èíÏßtœØO¥5@äž^¯Z4˜JJ`vÌ…ŸEpg4ÂèÊ‘¤ªuåO£ãˆá:ûÁkO?/Ю]µ'‘fc~?\ Ò k‰i׳çۗ—¨€/°T…êÕtÅ¢)3‡3 ñaRö°Ÿ PÞu÷><+E~TP‚–ãÝFrqu&¿/EÃl8Ë>ÜÑŒqÌgðúõVš>"Œ:ÒXyÇ™ßìé|àκTNŠ‰º Ã’ßÓ9­GÉGMõÑ:÷<ÇHgÂdþ0¥g˜Âúàp¯=m; Úœb±§Õ⦬–É׎¹ÄË>Ô'©<´¨hÁîx{Ü}møÎQHø £Ú£ã¤ÃƒpíäË{¢”¾"¤‚…‡"w²tIÅþ*ùÍ»ÇÂSÛª®‹r_d{=’!˜Žd™úÛ‚塽ºÂç¸ÃÚE7gMCd<¢p°#05ŽèM´›AÊèÂ’„ô¥²upÂitOJ‚ƒ#,ÔVÒ¤ÝÛL<}||aînj{ÎÎùV0}ŸšÜž„Z§ÓÕ.­£Ð— ÙAp¢²-ziUµÊ’DZ½0ñ@—Œ1‹TY!„!0Ì ý #,¥EΫ¡‡×):`v¤F»xmÝë’ÉÙ÷^ÚY¨y¼R©õîëƱµõÎ^ ‚«dP({ÝÑb¿—o¦aÛÓ¯m?\Χí¶x¾™T#7³×fLÅ#0¿rÑŽ°Ó¸w£ïšôÓàò§í/›+s¡PèæË?îûŸÇ}ZUÃú’í›X éahbz†8ÓÛ{[qPÞ@ ‹ÛÄýf¿¯øÈC ‰¯—öow#/#Øþ;‡ô–2ÿ)ÃÃ"È°àKq%Óö],Aç?uJ+¯í*}Àçè©YÅÚÀÿÏÝUo±ŸñY¡üçî/0 ñ]œwŒº^õnÁÑp$"°q!£†›{à+ŠØ@á¡Dð'¬gÆ÷T]$ŒjþßüCÚ-ÿ•3…+"7/ûŸü•­ý^ûþuUÓýëQË ïVI;Ó7ëW.ÁÓZÞ‰kÄìö‘ß#/Ä5pü4¸yº|¾£ùt\¿QsÜ>çâ‚ùÖ,ZC ýãYD}õø)úäòäþ2hþ'!vÑèårjñŒ#ﶿs¶¯Krpëz\ô‰½ÃÇ,¹Æ¾õsìú>Þ|¹s’’L9n^f †|ãÅo¸èx©Ó|)Ï<£ý½ÙéWõ”åX¼>¤öþ”‡ú½…±›è0!7º=x­ùp…™eú*wW_Ð{Ý©à*§÷´[¥¢Ûñ ÷!ôôzåÃWw#A@f€üW°@CÖv$ã3?¿éҒéì¨b¶]Ÿ'Þ¥(6I샭¯öû¬ï­z€Ñôí¿„ôk·Pñd |$!¾¤øBéQ‰Ñqw½ÏiŸ.¬áæôÉ2ëÄÂêçßžéyªÑpÀrÁƒî·MëÌ£Ç%‰7:n°>€¸'#0×1AÎ?g#à‰û>ýw×u»‘Ùò)É`;Ø#Žê ã^rô’þTZôG-2ïϯéôú#0‰ë íÓ™ÌìÙŒ¨INr$*TUf8κƒIÅã_îöYä¹õë¨tŽÿ‰¢9€°€,=¬¹lJ‚‡owÊñR%¦Ýxc=ÐðÛÀrNÖyacŒ2ˆ_q^å#0PZTPÙd¬¬ü NGAÙ¼jÅÈ+(-T2*kù{¤’$Hê°ÕåŽw1’·8koeßØý{Îå”­þ#0#,ôÂq7¾{ɯբJm©JÇJ'ƒ¹¸!·õ­ikß»Ò+/÷àwÒ~謻‰ô#0 yÝ)YÆ~#00’Ð#0.ž×º;’©j¥Ç¡Q¤hEKÍÅ›„ùß”ŸÓÚÛ#0dɾṺ3ö~|YA¤}«9½íPïÃpÜM3l²¸šî¤Ça™”hpßYÇUˆJßyÝ‹ž_&‚™Ñ^7xHß/º,p†“éºC˜óJš°=]ø5tžž\qìÇA˜LÅ:Ól¢„©:sìG;yëˆsÈ€¤ïÀ]åƒB@PïÁPhÄ¥#,·mH/¥¼½¦°œ¦°¹Ñ*xdñQS5æJôrTú‡¥››‡FÀüvl@öA§/_$øoÒWû^øcSóJse Žº¥*›LBég–£ÓL×X3Ü ¿|êŠ< €© ÝsäÒ{= ¦f¨õQ9¡Øø¡–yŽw®]6²jêŠú˜0€^Yéd›øú#B€gvÙ»Bä6¬‘L[ÊÉëä¶Ç ”ÙζŸ¯î[ Gp›Zä¹ÿæóxýcÍ«QOi뱕¹=}ƒ•ž*A$€Cñ¹ï†˜qH«»Ï(Á³Ÿ#/çä*µÎ§êÆLo¥±eý}‹8ÑúTJ¨‰^¤ˆÈöÈB×'2§˜¸qé)PŸMAÁñ,9Ø<ÛŸÎOÝ9ár9ã™:¶ºÅtDÝ¢Ï,nùŽyôÛ§ñG9ÈoÉrŽªÍÝ$W®ªU'I2gt©/㶑³kâœâ Ùz6,•4éœ!uf¥Æ(”°8yï{í@ñ‡—5Io|päYÃ6ß7hóˆašY—@É…Ó•Î<…r:ƒ-{}³†%lÉÃKœ,gÃ1“ÁZ¡«(™“ãöÜ{-–KsÇj;;ïÂqUñ»ÏŸwÑÀÜ [<‚iŠK3¬LAtâw¨Ñ@å#,º7µy‰kÕ—E¡˜™ØnÑ>?½F6?Ô9FAŸlP]§à†\󦨵Z#,аYkgh{:¥²qŽ¬òh4F#L§•²j=–ég*ìa²¡µ³]DÓ©¤ó„¯O¯4fÓ•†2茿clòÓßÙ‚2¸Úáû;'ªò?ÕÞIŒêI6õ¬gxŒëæžoOE+NÈ•Íù{¥Ï~þªNo8JƒãïíÒô»z§öã“rI™& Ìõç·”ÆnyË{ºqœ&ù]¿má×¥ïiSu ÍUд¼Yy‹,³-ª¨àd!#0Å—§@MIlg‰A›»ƒp7ŠÕHiò#L†˜¼[hØyÛc†Ì®³ÉsÍúGØ]œ¦[ÃÀúÌ?{w…rõNÓˆq†™H)`%\‡ñkûòp›{{ =ã‘ù(„7iô y+cÀÁI€ ÀXžž—²¿¿ÐÒ¨{,ø#/ÃkÚŒG*˜[C` ªd^ŽH²²õ#œ¤`fFïÓ[(æU¼Û—d·—Óðøã/#0)j”&hùûjcƒJNº¹ïûÞÖÚÓ¶i"@ü¾ùÞ‘øk4°ã®ß®SÖãdé*$õN:{‘Øó‚ìœv?¸„Å®&õ5“¶x÷°ëÒxH¡¡žUaø¦ Zøè2åÎY÷”léˆlÿRºi:Žyc†6zøS»Õº<›q‘“|­Hóù<Ôºg¼GC€ß]˜ az°G’ÑnC<‚ÉvJÁç×#kȽd‹TÀ{~6\¸[Jzƒ” lK\ÙQÎð°Pqõ9¤¡ÏTXŽ8H\ôϨHðŒ½«©T` M׈ â3³'’䟦®´ñy4ݦI[ êPw[#/ñ ©Ò~­%!T#,Ù ¼;}5ñ ˜I°#0ƒÊxyyŠHŽMJ0Ty$„’O,¹"#,ˆÝU£®iâѧÈ.2«2ø»ò;÷˜“:®<ÿÖSðz£Ïk!m×­þVu#L«ˆ¬£j$ӻ¿ˆºà“Nt^¦ºâ„#,8ê‘Þ,Å„g®a:;l`Dõ k\LSÕ ¶N#,Wp‡&†íñ@Ãc kÀÅr…zù»Úú¾=lëëÊX‚hêg>/M%:aÝ H¢ëáf«öãìiº¡Þë¿{©­–Gü7½:úÓé;~#0þû›í7C³ Æ^ú%ب]èç{« îZÛ›—s`“çÉ­Æà×-m¼tUÎDHϲË29à»P!ǃ©xí*bQØ_0º#0µÉÇ â-¤År”90®â)ž9©¢È±×pWå~³ ½ÍžweŸb˜ òÀ=ÕådKIóÉE.\„„nîwTk©sÚ,$T È©ôgËÉ|­,Ãn“·LÖ9~=J¸_=A[-¢šž2!r(PjPÚ¨#¸Î%10ǬbÀÀ8Šé–­ v‹oÂMsäîëã4yËÃ#0g:AhÜyÞÑH»SÌoæ¶ù ª¸#0…Ö¤ Q ´#,Éߟ& # kÕkÜö¬ ¡#0ÞŠRÒ–­€Êåv–¾¨Úy¶-vmÁœ~¡¯îâ­Œ‹Üþ_Mƒø–"½—™®?qÜêty6óSwøD5úF„¯"X¼ÂE÷‹lƒÏ`…‹â5™è7åæçA0z`§ŽTEî;Û8v7ðìãùY^–|k+Ñ{£Ô}I&ÿsÕ>Ýј‹ƒ(šKhÈPT4ctÇCûÆ5žŒÃQ—:«ÕÃ@·¤0Çuúvn·gç®SQÛuŒ%£Ñ#05|Ò;$±ÝelêXÈp­v ³ÂöG¥œ®ië‘ Öûy„Wâu΂{zG}pÔœŒP2—0ZÏ„³sàñV¨_*êpV+AAW'?]$•NÎEºNt3‘VÈù\¢Á–ïÁ0/×N~œ»}FüXú–›Äž•˜Ö~gVÜÑ›=yhhÛNfäCc××6²*Óx¸†EKåp±ÞˆèÜH—\¹ÝÉäÈ<ìŬ6ܽ#,D=HºŒˆèé}hÚÖ®ÊåYª(§—³CÚµyÙ†¬ˆ²Þ[®=Ük/}WÖ^EIŒvh5µíœf¶mhæ\[EêE·¡A[ÍË*C¢qxF!eTÙÅqD‘οv<l0h(ÉÙÛÄxoÔB&þÑåiSx±í…w_dŠKÁÈ5b¡9JC`¨ãª«ùrÛ…õâ,Cƒðƒt ®|ŸÙ‚Oo|Ù·ç¾6ãYs´Oœ;]»“ÅðëSÚ”#,&l£J×Íû-ï¹¥ “ ¸¤kÜgât®¶"2yŽ [Ž#BžóS¦·‹n¼8ÞUÕº:áœYŠ ©`Ì»”Y\ØÔž-‹'š¡µv”LF˜áZRÂoªÌ*±7 6‹5¼Hæ­;ªp:‰¤nQv½0vë¡M:!™3€¢±$Yh# ¿AA\é B½¡­é`n=¾4^þ¯9g÷¬Ozr)5±p†Á€†=È1'låM'›T·ë#,¼"ˆûøÝx™§gê»s$ÖzÀFGúýŸ_íë¿Ñvã©ÎÀášâ·ªB›©vºÅs”ñéè†èiõ.úZÊ h)¤!d2ÌþW$#0·ÔºšˆoÙÑÌË€¾‹]dúN=º]ˆ` MÏðÑ«ð÷{‡¯È=>aqÈ9årüÁÓùh£çÌ $Où,³>è.®Sà‰Uj¦­ã½FX÷¸t;¡‚©$„a¤#0ekªêdyñ%Ã"ÿM‰^ßÏëïÅ– *>³´o#,“øüUXîsÕœmâÀÂú·/&³3´e`ÏóJub¯Ëõ^^Û:yñÝølÿñâÒ¼e€Ç~À[°^`EÕìZ -g ûÅHŸÉÈÝuÇ=ááIUA‘ÉùŒÒçá›/Ê#/ê±ož˜¸ ¿™ßuÑ­#,©ÖU*è)Žã«ýLõ×ßþÀ=ø‡߀»õÍI?­è#0Ì` #,¹|ú¡¨²±ýúA#,rÿŠQT”ÿ®8à…ˆv诚üì$v ¼#°‹+pÌ8#0#,©þ‹AÇ]³ýŒþRÞ;6=½–8(]¹Û™a·û¦û&‹`ÿ…@£ò†. vƒàòêFÛÏ´Ü*h ™!©Ö܃¼ð#‰Ë˜•éæmO«ÁÁ¦óoŠxbPªgÚ7¾Â¨…~#0QƒPÔÖlÂZàòáÉl"`aà×1±íPë÷£ØAÌ÷{àúŸ’ýŽhƒ™¤=BëÊ+þp°/pwÀ»ƒàN¡3K-#› ܹüw¼/’l2¶u°, èÁ@}FÂÙ!:NÍ«’`dže=kÐ8 þÜTûTýk¡Ýþ=½Ø)½2ŠxÐ=¶¡Î¢±@¬Þòêá©È#0‘!ô—µ±@3¼€LfWœóœ›ŒË’HFóLê¯W²Zé?³Å- W#0#,*ˆg#0ÔJâ:5£êþ÷ü>ÿ_¶ËHÅOßbÛ·Y-õI#Ûçéó{Fåº õ¨c©îbæ?Žpï Rb•U×¥‹¾õ—¦$Ø}fÉ kRSy[ãý;x à|(`/¡Ë‘Ïõ’ÁŠØiâ>„7Wå_´¸;¹ë8jÕE£4±Ì—цî8$Ö‘¿«ù#/@Úka¬*p=ˆ§"|6&œÂ”b‹±#,óÛ0Ÿ½ê)#/6F”ùò’ŽÒ2~h„…ú|©ªåfÐ<åÉ8u}:€æ#,æõ'QÐÜtu¶ìPÕh ¾òLõMš£jüèn;ôöœ/iãr]È‘º–à1ÁŠ#0`ÂÖXp|Ào{ ‚êðRŒ,=>’Ù §0çs: Ù‘#J±¬u-‘4ˆhUpÁÃÄl-Ð?"Oëý!êl€^/TûkÆõpÖ’}ßX^äÿVϾ^+!”}BXþr¡€xÍS†\BêrŠ31L•ý%ï§üp?g€OÔc~Ž9Þexüñ±ïò®¯âﺣ A$2­@9¬IpîˆÇ¹æRÇý›vÞ­g^FÂÅÉèÍøoK1!Û@‡RHõpi2¦Žtµ¯÷——–¸ê·ßvÝ£ržÂ+ !) 4^*÷û÷êr#,$#0"Jé>NW¡ÔyRR°Àø|}”ÂU|mòñšŠý#±þ:Ûª‹ôŸpøÇ)O’"“B+X4o‚ H˜:LÌoä'#,„\ Ù=§· »&è?lÎjïü™ŸzþÞæYøl ­Œ‡Ÿý»¿i!Âf¹´u2’Œûr_*FfÔŠýĨ}~Ó³ºht @3ü¿ÈURò¼P¥1ýÆåA%S•.¼®{#/­ïÕ;ji@:ŠptvÈo6Ô¹r~÷‘ÌXôƬ—kçØ`2÷-<ûâ¿L9²:I#83Æ1nÉ%‚«vî’I$,K½±+÷{wå› 7—}»àvlGgµápÿõ˜ðyð5í#/«ÒÚâêFAB™oJ¶Z9<ŽL3e#/Ѻc,¥¶b)¬¢â3>ÔñŸ‰ðú¹×¾}ÅØþŸÎj@¿Ú¡Äæá#,Heþ=¼ŸõɇWm¥Uä`þ¸Ñ×·¨¸“CŸƒa(þæü†-0Z2_¹êVoä~OW¤ùð¸Úñ¬‚‡‡ã£²€~ŸFµÌä?0rF×LK,}îñõþïä õçCÆ{NË2$ÝNó"ئBË™Kdê»î½Îs«=mÎ:iŸS¸PÄ(6fÿã”4~Y/òêo-¨Š0DŒ!2Iœ\älî*--`‘CŤ#0#,¼W¸°|xíÜȉƒ¼‡¿Ì˜.DC!Ë¡£¡Z ¾¿¯ýýk󫧸ˆDªHJdW£©M¦äûqTîœPêí!#/ÒËÎKQ}P+„!ÂáÖ b*š<wµŠ+ S>ô³à\5Y#/]öçb’z’•05¾,>YÅ^Eí“%°ðXmÈÐIR¥9™%0¤Ž:Ñ’˜1èddh Mž$giÉíî+Þ±šÄ>²{& ³É¨…#5ÖçõpUô†³s‚vyñ –=¯I‘$73y3Ça“ÒºS‰u0bÈvHŽˆ}¯¿öu~ƒí~ÎÁýØï½tWzèü\ó’‚Ö´¦~œ#0Ûq;¸ýÏ8ׂßÁÃt÷Þ²o™!ŒdQDPÍñuUtfÿ‡Ñupã™Tj'û. ‚ÑèqÑ 4ÑUWB•b#0ºçîŽÍ¼fL®Zi#/dáÁÙ’H¡$ÍÐÛX¤ÅLÖ³V2jÕ—VXWuVð‰ tJÜ¥#0ÓH‚Ejš”Ô²_äÃ8ÑÆ|7½ã«,hÑ«HÍ¡­¶Øþè´G)–\}ß j * „‚§8÷d%Ęá@=ÄÒQ0Þ^çSÙûmý~gO—ºDêˆOÉß#0|kÏ™¨ï‰"% DÒ•€­D šóƒù{5tˆÄó˼ÉI~µ~ýJZ,;¼=©÷#îûÐãO¼é@–‡àC£ÕhÞ„CúÜÁ5 '1RýO!ôr×Ïä?’ü¿‰³{\àO±<·ÐµôZFk.Ù¡UÞ®Í`Á$A¥²ŠM£çL“§þ¸Ö3“BÉ[Ü\$’I6Ä0< aD8„êîe%5º&WÎf^$ aB‘:ÓÞtìmžªoËc–SØvøŸÉ9jyâ’)ü7ìÑ{Ìjç×.Ov"j‡dþà».YÌðžÐš…ï*Ÿ(sŒ‚¬¦˜½~½tènÔ¦æ‹R¢TR,$j…-óáÊÖ8|`(p2ú_iÚÏèR‚Ï_´Bôlêí ìb;ÂÅCÜ{XYûÅ’¤rà{Ü$.ŸM¹òjoÎzmÄ®úÆ÷}r{.vzŠ¢šZ¨HBC\MÉ#/Û€òš2ÝA²êÛ Pß[7uÕn€áe(³¥&'âø|MùôµÐvƒ§Ô‚ßÄ}Q8˜ý'ã9E{„ … ¢†ÆöÉŠ…§ù bôDŤà"…Â`‘ëõ…ë"#,ä¸ØMÁÜ|n­ñ#,£@]ßÀSæ@\,üuႧ&$W €è»«Æ &·ÁÞ€¸–ñ[»pþ#éáuà‚•CG½EEu|íR|ò¼ØÞ—¬,ùèTh€¢_nÐO"ÇAõàXÅûóÁóýú5>F_=èî9ÑŠñlú†ÀXaÑïÌ5¹"„ ƒøŽa›AúœN½æãìƒã¾á1§;ºÿSó:ÏCZ§(º±ý“òëu×½?x‡èõcÜ©ØEJH‘Á#,n|;a2Z5¼<lHŸ‡OUqðwž`âîhÁG Û·5¸jÎÀ;½O§©RÖŸàuq6¡7··¸ò#0#0B)b‰ôžA5ø<ý§5º°îUa‰H&¾:\èC¹9>~†÷‡ 2 RÇ̽<éI(ryCoOsUó¡óêæqüXýùû öÛä?ÃGàoˆþƒì+ìcì“=¾ûcÊc!X•Œh|Áù1»ãŽ‡wÙú¼çoÄ«én bSbµ2P,Ä$=8FI$žÐ Š§ }©wíѬV"9¶€,‘ÕæüâþDŠIõh§\p5 hiÛÌ÷Ã<ééýGoÑA‚=jgÓ¬j-Ã[[j{ür¶C܆73£nu!rwæ'x…xýžèꘔÌÖÿÊ}ΓÐPh<~ÀÍô_wÃ!B&Ý¢iUùã<’2M•õYPÍCxtÜCØtל#0næÁbq#/…ƒ™jºoyÇð99”x#/"‹·õ Áå¥!õd«"jÔDJGÊJ87"A–~¨%ÄÚªšPN65…]4%2ÿM*'j©‰IëõüÒˆžj¢1ID© 8êösæçša(Á˜Zëäm›Ð‹c{ Üô˜ó#/Ï/OJm¿'")E^ü†“,0dI‘Yëð#0 °Ù?YëOÐt—bXÔ!EÙÈæ]‡©„ªÙAkÅT‘%lÕÝЯž»Ÿ£¬ô¢xÐ&aòÉêµp;7ðœÃa|P{M§f¬;ÿ"[7d¯5U(½ÏoÉâÿ ì¯Ñèú|BݧÁÇ·)+3lb˜¥~K@B¤Ÿv ©TÐäŸâÊŠ£U”+ÿIÅ(Ô5\´¨8(©u½ä àÔ&i‹¶£.Ù*©IU¤p#/žoŸgo„ôžwÏ=­ø¢ŽÅ0S’Ñø.—ÀûÁÒ¢[ú>KÊMm†¦(þäËÚî]æ×Ý(T÷CˆÇÛx ˉÇîùÎG¸óŒªQ¤!DZŸ;ÓöÛˆ.A6ܳ®”éúƒÊ; ˆ]ss=SºK’ñ.+ó¹<ÕGÒ »ÒÔDO,¤iñÐÝÆoéä8#,±í.X,X°QÄ,&®ä/ó˜=µld»°°9™Ê=Þ<ù~¡û?,߸:|hõ³ÆÌ$³>Ô)}“x#0j\¯4Öóĸü‘‰\qœˆ#0º¨-Á#/CºOÑ0FÀ0Ð|aê°3)AЋ!›6÷C=¯rWYï[E«õ–Ûá’B#0AÂ%5J‡b‚dyü¸ª!Áƒšl5-~ƒ·#,Ô7žÌ½¨x‘±‰ßá…Ñ㑆E&AH< !ŠÃj­ ê€:oµ#/v'œ?Jvï4S#`Qñ£ad  æDbÐZ „OéG#,õ}<¾Åø¥énHõ3UŸOÆ!Å~É%#¯gêùîÕôSéÜl6B64JfQ¦úûÍ_\Ö½o#/«r3J„Ùl¶î¼ó.µ<ºçZ@dd ¿ÈoNAåóô}Y_àÞ< Ë¡w×å<Áë}½j×Æ៵˜‹ô’-zOq#,°d¯'€0ØjÄ3´Ã¬Úýº)VCôJ†Çj"˜B Š#,PL,àÜáRŽ#0CžÓ4,D çG,-d'ƒx‡š¨ºîäë¼ á'›üñ®¼« ^‚ý€´·C´h&ÍÉÕ¹?<ú褜k„nì “t=WSxè(ê#ÐõÚQ·²![‡“AÑÐŽ®YÏLì·;}vÔÈmÉã#,Úb›°¶psy­§²â=$:ºìcp•ÓU3èÁ,="|?Ê®-Ý5R•QE¿…`©ü ~ŸQñH>Òª”òóíÉSÓÖ{ðr¸„¸<=¡Ò{}2ìù©¤üDÇb6ÏèDÙó…ZZfi“ 5!xXFˆCz”z–IVI$}\û˜?f#/áÛ«ZÐ0ÒÖ©‰ƒ2¥¡ãd täBGÔoâ|8ÑH|XÚG¦ŽŒR:G,qqBùtþáç:uÕH*àš`„"Ÿ (|HžŸgåÑÔ^L$'#,ìßµgaÞ†(Dh0ëéÛÓ†?}w„5ËGÈ!ùæ/_/r¾¾Û 2ãJx¶1®‘÷ï…hŽaßH¬Œb‚’Žäô°Â%´ USN½t±L‹Hhq9ŸaŒ¿a÷çÙ_p6¬Fÿíôcø²¡[Ò‘ýuþÌs˜¨•?Íò>}Là쇣)®D7ó©¦J”ƒæRªÑTzNÏ{ÄDÚI&²74ûÔV‹§Õ)óˆWçC¹A=¾Œ—f(¬`¸FE'!ö°£™o*˜×põ c%Ìl¤$Ì/æ ñ Aõ¸uÀöHQáÞù»s¶?H=Ï¡V‘JX,‚ƒ…äp]±á™º \yœÕÉ<€ ÇòýÁc²á²}¸rÝÚk¼ä#,;Mâö“ƒ€©l×èû,—F¦#0 –át|ºŽ8©Ì1};t•9‡0é#/ïl2qÝ$…”[‡é²›#,±ÑàV)znn²rÀb"§N#,açÄ:JåEXw™Š}(,ížÕFø©·É×ðq…gÃj¨Q6q=8d‡x5Qc!<Ok‹¿WïöcS%oF÷¶'È»lÏk6ÖWüŸÚ™Ö¿~N'¹!ñÁUÊ›*Ê#0 Ù[_fvÅY$ÐTNPK¬3o=¸Ô‰ ‰Î{Q#0Ø›—õNÇø‡8œ#V`¿=;µQŒÀõ‚*dýri(®Sòª--lm,L»XWW#/òû;„sª*ã°wö³àeÁ}¢Ò˜xãâç>>¸¦†/gµ$UÞtWJüeÌŽ9¼Z£™èåÞ#4šÒêx’€W¼ í.=8óâê‰ÓP0²8%w[z„ôÓý}¸Ô’L7mB½JØxï#/ÆåÏó콸ZdŽš„^@ä¨gCò"¦Á3;W‘½¸Ò0!2#/^6‚)a€ÂJG‘¾ôWß èCÁä¡×Ùæ?Z#0½AùöhÐ؉6I[J­‡‚ø{x8¡ìÔë4Fv”Êæ~sКàf`ðT}nˆQºu'Žvîà©”7£#,ò.Q'´tú¡á#ÇÜl‘`¢—/l$ï»wCnA&Ø ‘>ïœìóù»ûL/\ìÿ‚)BÝ6>Óžß_ù™‹ø­¦[é<weÄ/®œößt lo tòûœø÷ »F—èÚS'”¯O¢ äé‹CFÖ Îh¿Ì“éÛC?/ºÏ¨ùe£&¦d³ž6Þ Ùçÿ'öÙ§gX\hã7‹ÎCwÐåS;ÿ‡¶ÚýlÃ7<][ÓøÅ€n¼†í•/NP¨0€ £—ee5hPl;0:Ô‡5ÇBàLˆnºœ}ˆà…˜oZ ð$i–h1à๡ !`#ÐB)Có»¸À«Ü¼Ù€~?µ#/¢Œ~ØŒŽ\"H±‚Èîé{ƒ¨ ª/hmKÍåÚ˜÷9eê-BƒO1Òpvôg“APÛòüš·Ncdô…<‡òÇ ã(œbØè0µÊ³:SƒþY˜“ñÔj[ŠÑìCÐYV-KÞà\Žì:·ñ#0/@zƒaL!; 3n\- ¾Ÿ?¥öB3ÛoÝl<Zñ±j»çÕH‚6ù˺íç\Cn°¬‘ #/¯Ï…4Ìϱæödš„±Ö÷6Þw¦ùÒçQ]Á wc?ÇQœ¸G3‘ÃZÅà¹J!âØÑ)’úÿ?Ìîå­1ü Tº4G4ò…­¶ˆCiv½_eYňrI%›îÙ¼ ,b±"‚ÃC¶\ûºåË;´½SÏ/",Ñ_Å[zn€u{<ÞôCÏ+²ÖMè|ùûe¡Þ¯¡.@]UúRÁÃÜÓÖ|ÜøœA”#,j}ŒØ¾þ¿mÅàxCvý MÃöùèèïÔ¨HIµI!\ü¯ÀvדÔéƒud-Ÿë·Qþ³³s²Ne–Ú3àˆe[j"¨¢£ccuÄBI^_‰T”Aý#/s¼¦·ö§ö80¬c8i/ãÐÐ,ZÓw“óí\{jnsÖ‚b#/ahÑüúÏÐqÜ­µF“`ÕëÕæ®é_ÒfRßg[Ú¦CRHÌÓâ_Ÿõûñ±ÀîWŸ?#0„¦ƒ´@ºžÑ…?%¸ü ‰º{#/”J¦uÑÏ&­…5ç@ZI¹Êr ¹,ä}/kÔéù:áŠP _TŽþÿÔ˜˜¿8ôÀ>Ã|5Ï»¶ôÊ=usèz`u}ý;'ÝDŸ¦0ûžð[ž&=ßËß³¢^tUÿnãØSY†y ¨ áøü<°÷3wòïó!fª¦ú϶äßÑA„`íì³´© •'îé¡ŒÑLŒ  i*4OQeIÓÚT,Pau,‡}XvcBœuýÖ×û!Ôlj» 9ýž)EšÔù~'ã§_?Ôo‰Y"&ÕÙ¬sZÈË°‘m$#ç´öÎçÁ#/ÁhA Ë#0<6ßtWrVN¯â½ÉcöS2d‰€óy€r+Ž`=¢RëÕÐ\½ƒú+€—¿ÌåF÷¤ËõfQ„¯Ýó°VÓóàQyDJ>è…¡ƒsS7œë¦e²yLï<­ÛÒËF¬2¬'Ü2dA`¤À!Q2Ýa55|Ž„ÌsC‰ÉÒ¡¨QX'B#/L-%LÅ,+*y§ýA6§öÏ‹43R£ÿ@Ìþ!² *kf:ñ£þú±F÷hpˆÍd¤m*¬3XEé–µ#/wb³Ž0\&šµkpoFÌU÷ 8 ;B1ØËPoº ”ȳ‰^¥ +IV#‚ä %…bž˜1ÞÃ%’ħAý²þïA?µ5öÏå±tdÉ0#,‡³+â+5¯%Ÿ"~;#,«ô5G‘Üìð¡&ÿÙ&+ðÏðrȉ†ÌßÙþgtèþ 3÷3—MŸ“i7D|7¾»èsámÕ¸8„AêâH#/#0KE’ÒZEšqjvuì²Ó¹Gcü×Îøx·ÙþÒBNs‚Á÷zx€—ø|ÍüîÃÏ´kj#ø^gÏÜðœ#/Á͹qÀ*Âë´µ¹=´˜É@š^Ed˜¢¢ð9ÍiüØý'Ÿ¡Ø:éòéÞüÄñMïa³™Ä­3jXu‡S©ùÍ}%Ì~³JÏeÝÿÝgì ¥Rð#, œ0¼*5#,¡ÛôÍLjL¦É$”‚°Sðàç$FÕy;‰çñE¯CÞà¢KÕÓXy”2…º÷ëU4Ü;à‰Œ¾¦]YêÙÎ`ð4ÍÉe•€æÉ(3ÜŠ0R<¼j…] ’üŸÎìÁòëæw$!¡Í·þƒ¨¤\²L¹pzï·™MGÍ8ð)4xû:›{AŽ®#Üm55~E÷¦3uœ‚MíªÊ0Ïlÿ3ÃS¡ŸØÛ¯Ô©1}–òZ&¶±ûa61Ã9Ôí¨,áá@!Ë#/ì¥Òù‰¨¯ØOaqrkà{zÆÒáeÈ(tŠ]ÍAzÙ0»'[îæÿb]çi²÷=Ÿç<‘Þk#l¤ÍdƒÝß[øÉì3EÃ5‚C° ë…Ívå­…óNÍúˆ^Nt{ÑRá‚zÝE÷xçÝó;Îs°•ÀB`CÛ »ý?Ù×€øIåæT@Ï…/ã7R“õýVéùõ6?íïª(¿}ΘôBU8ÀÛOã¸w¾wÍö`÷¼¢øG:<?"¯Cê»T‰>ó%Y"±Ÿ°Çm˜èšÛç:G¿åö†þg{©ÎâEm¥Ê¡U—œ5ùtH}׆8²ñ…ÂÇCèf«äã{cm€Ç“°é‚¯ýqa‡ø?é‚çÀy\j¢Žäï‚òÌ6ŽÇàiXÙ1¯F¶‡>ys8€õà8#0#/Ô®ˆƒ[;Æ1Ž¼¸Ü««Úë|#/â:Þ"y‘Æ5—Ñ ]/â[ñOÞßÊBŽûîa1K”MßAG[ø¯“ÛÀZ\¹\±xx…Œ¡Ì–¼4wÇdô‹•ÿ‚ÉCÞ 2áQή¬Ö"øNZp‘mÚšiSål3µÒ–¦Åé UrXz×g8Ä:Ds¾ŸYÚfúïpãÕÛÙe¾ö†;ù»xåï&ï9¬+cM¬)Õ$#/;ëÂ&s‚…( ¢²5§\åºá`”ôF˜òžu.­n™¥1¤Í”ËÙÌx~Æ"ðoû£á“l.äí#,ëM˜à¥ðãhöÅÅöy®ZçE¦Êç…òlÏ‹ÖãPµj½RéAžÙˆµÊ#/ïñáÙ#½wÓË/yWã#/éyòíǤÞN˜»˜†á4.ŽæîÛ,OdS®ä.™Þ:d3µ¶¥å·³ŽŽ'­Óƒ[j¡Éj† @AbŒLÑ Ô¤Û5%E¾Þ¾7éqÛ“Œœ+8eu¨•G.ì·$„ÙAßöÖÝ#0¤[ïܨ%¬†òÒâ·¼ N‹ …’Ïþ3#,H€DvÝßëóõý’÷Y¹F"¡^®¬ÊöðØý•AyyŽ…p—Sµ0‹CúñM+„±¨Õ LALHé|¯“nù·Ôþsl~ëÏ›Y¨ß2FQ¾Ñ?~p¤¨/Û œ²ºab46BYá ×GML„®säX½¹ ëž©÷;ß»©ç9\££³ÏhŒM<JSçjBM·&F!@šÞ@9#>µË̶Û@mgÙ H¯DØ ¢: tµ3l¸]WÎû"#/n_¿Ãô¼“:ò>E(ŒP(Í>F¾– Oú0P>Dùþ¤çê…•£¿otÄZ%êå¶àz÷Ùºǜ‹Ï߯È#FÕÉ!µyX<„CèìÃ2o#ö½7ÁÔÆ|>J;ã#þÈ‚æ€ã™ ¼wcK1æg,’üýÉïf hêÀKFÀïó}µ×6x{v#/Øqé[ëßfŠ’ø9ÇBjQ â§fßÉä9?~ýö콌z"ö71·\MÎq:[Z}TS‡ ñM“^¦~œN†#0à ,$ µnåÓ{fdÆ'9N\KÓu.Δ$í@:hCëÚ c¨ôy¬þäÓ“mu‚]ÃÏv˜¨㇉^6ÁɶoåkÖ%òý‘»]Æ˦yÓÎ%°¬›Râ&ć}7<ìå–Ìó@¤[\L¬MDOŒá«>¬FajÉ*vñãQHŽ4Iz©#Coa¶Ê9%»ˆ”‡AX/ÁË@È6iˆ±–³¼s4®`Í1Âr£!Š„†_~#0>#Ðø±…‹Y‡qæ"’2Æú|ÿíƒæŸoßãÕç?…íÖrc¹P+ÝýßL/±ÿSÊ\êØ¡•ñTçsù¾ïGQÃLÌÉÈ€T>âó¶+í÷[©Ç8²"dÊžªOͯð÷\þ¿Ú>Göÿ?ò>a¸‡÷ °só¥àã|#0òªª°ÿaç`]L øÿ¼acUÛ@oØ붉ĭÛÉ׉ÌÐ6ó?#/»‡ ÍÔ)Ý“úB‹·7¼J#M!%"F#,Daýï$þm Žðã¤Úèu*Ž¼v’Ikp%hlu:Ü¡„Á°Áf³Õb6I.CÑy‡³1G¶I`È´›–òg¾ÂêH4|?Aܪì<“n!P!t·Úÿ°ààÊ÷==ãÈ5YßÝŽÏF(;±\Q£Ì’=ˆ(ÈÄO0£Ìþƒc¼áü#/€½"*¬`ôªVª•F¨¦ÂΤÔ; †Õ;Ͷ6#,Ðc®þl4Û¶„âz¿…?O×]Ä…8%›@Ã-î;gáåbyé3(æö9å#0ýjªæbÃÓØh{ƒ;¶>ç¶xIZž†›RVÌcŠ½Q6AÉØlò!ÛMY¢“U8²GYÕùpwaƒc#/ 1¬1ÄÐŒF"ª4aF Ì4Xpl¾À2ŽV4\ã Ý„}!œB °%–Å^v¾F“Û½Ü@^[@Ñž^ŒÎœU%@åËmg›´…ÀRÜÁ˜˜ŽtÜ6„ï5*Ø¡@'ɳÆýú³}ä…:suqjçZå÷ü÷:[õ„aæÏ¢#@ßïÎî¿5B¼³WÙ°€uñ¨X¡ùD“Îå$›¡UP´2hÉS!‰«¼½×º÷Ï6¼SjK´`D7¬”^׆ë…‚ƒðaõ#0Eî[‰+W÷Ýq#ÊþóúÑø·Ä¥e,Š/äólkeu›º"ŠÊ•D²0~ÒêžÐ¥}#0r4’!WvÝŸD$ï;ƒ#{ûÙ#,ži“CÏžþ‰  M ˜rÿ'a¥¿¶ÃŸÍÓ¡ª¬V"#ˬ’räXõòAl‡¥È±˜ýd68š†u$cm"`ŠÃ•‘Â¥ëÅm«#,=\Rñ.¿º»{™LkÐg#,ÈŽç)wI†n!Æ*x6ý#/‚‡‘È Ú¨ü!^ªÎ0ì÷&ÚN©¬3(Yo']©ª†Oy#00f# ‰?aSäi#/`™PÆ Ç·Çu·{ŸËC×n:0Ü€‰©x$"Pìní·`¹öœ#¶#0·ÕÙ£\#/¾ê/VÛÀò^ÃTD£<ÊÔ_¬ó^EÚë4è*Êɱ?›nãzXUZ÷TÔé„2ÂË&ò.úK!wdZ#,8›ž)s›¹0™ÉIAwxQ"ì»9gƒ”Ò5W¹!Œ¦0’¹&Ã'›ê9àÑÐœ³1ŸL/sÄ;ü"«Ÿ¦+µ\e89dxdŽ<g-)ÔêùëÕë¯G<Žm¼µf¦I6Ï+Þ¸|ÝAÊÜV[:ï31"1Am©ƒâná‘ ³D¦Òônœh]Xm¥XQ#/^ŽÛx<ÃIË56ljØBˆÈÈ7?µ‘Ê™í^ÞPÀY8'PèjƒçC>%×­ÈÕ…„ÎF'Aw!CßüµMöú½ští™Dë¤Å,düp#,IIÑ$4ßÓÖ]úmëàHC]¨XDD˜2D;O©˜± êã±îñѵũwцösoE(¢ ´ï ã4Á“ƒ±r2à .¦3¤$(¯5š¼Èd1œLæ´êl±ŠZìh<@°Þ‚%—¯ŒvlLxxkÎ@ ŽçG=# =¡npHk¼/†‰6Gœpë+Jœ£éˆÔ:°®¦ä¹¾Ê>Z&èõ#,9!Ä>?Aäôù‚Äš¡°ÕæA()Ølt&$tneøºî\ÌÈ´ÀôÛhl`ÀùŒÚÃE„ öIy·˜o:tñ6ri=3ù›×¹øÔØ(Ë·³¶¦àædU{&G4À£NÄ…IYXª…Gc¦âêG `ág¼î&a—¨²tˆ%Jæ‡83+Mª.³@8éû*¢º¨ÜS¥ëpSi6›±&¥ÏÕ¹$¢ÂܸÞMÉ-]#/ÉMBÁÀ„*P‡jzʺJ™¹Áœª§Ülhiı”æ-þ øl¦n|tfca;A¦ïy÷MË—èksJ€ÁJBØšç[Öö´ÌraU[r™iuºDò0PcK€õ/‰#å ÉÈyI#/G¸ (D`«,<ÑÚ3—‘() °5šV8/X÷<'%0åQ©¸zŽ7”Ú!¸_e±¹W;‘2±“4˜‚£IÅ…Z©“™G™ž°å¢êt9ÑÜ`5Ô5ê÷xk2˜Ëb¯3.<ÌÉ—332ÆÙ™Y™3$•c¶g’^Rýú­¶kÕ­¡HXfï±—9b¨jŠ $zHØÚWΈ–#/bÓöùoÒjÊógtv!è˜Éåôe»‹sÛáÐ…¶9OÏ:ª5õÃV0i¡ŒyÌ…Gnñ´OÆχI­xxãK-×EÆÑ9­ÐÐ4Β蟆°kdÌ÷ªeù¢AdL!#/Lá­ôæV·<Ö»5xa=L™;vBP sÜŒ÷¦#/‡´^~‡Þ1Zšm6“#r1YT’UAD¼ :i0÷õ>n7¢×j x±2xp­HÔ¢ÂA‹ˆX³3¤–ľœÒ1­›eŽœHøÝ-6ùžÖyÕS`U,i–Wp†YŒÖU£ˆ˜†Ñ…– hˆ‚óÒu(,Kþ°Qs¤ãª**Ñá^brÚÕGƒãºæ]Ÿì¿i°#/€…IÞê7Fž²ü|£‘i¦ÒCKHÍbîŒ@i#,Pp·”¸BdÑb#0 }µÖ'‰]0’P:Wq,bLt#0á8M‰Eë–棿•Ê¹_›|&´(±Û`æ‡6°ŒtJ›"™½jY²n˜ßmÖo#/—#/ÓrJmÄJlðskDØÒp5‚Ð6“FÁhhëÖÇ6Ç–\¹ ±aïàGºjÆÞáDö›„Üà£J(‹Ðã$á2fdçqªºDÒ{pM¡$em½Ü`Ú½žÌ6¡… ÎÄý;Ï›q¨2„d¢4Øuw&Ppd†Hc’VÚSIÐèªL‹¢ðÎ`¦tGgƒÓƒGYn„e™ @h6m€¼šî×D p­6'ÿ¾7 qRV..Ä8âjƒ 28Ó†A•ˆãŽ‘¨ÊöCý/Ѻd~§&™òº¹¸K¦ûF(ÆÂl‡p Ðå•s ¥îl¿;_#0«UÐ !„&#/ö ¥/6î&{ˆŽ™º&)°àr}ú˜ƒ\`R$ÍÀløˆ„x£*rChƒ$¾êsÆ1dT\³¸e|¤a¬Öɸj)±¾XV9g>#0†#/+d\¤¦«dÆV#¼]™Îo€gÉ7&»Ó6ínR\00Sˆfsö6®÷D¤ÕĸíN=Œñ½'wÆ©ý—PZqÀíÜ—ß“À ‰"È(Á‘€1Š#4&¦¢0A NÒà{NòdÙQÂfÅgA’'‹À1=§:äS_?L–·Tg#g,Ð7‘ê)­í1£«Æfe#/™è,NÊ:Cpo ݈p(UÀ¹QtLŒõ)¸¾îP{X¿> æý(RŸ3hàkÚ˜ ×ãׇK»rWÇÝÄ.\‰¢Š¡ˆ­ÞÞç³Õ³Bº¸†±Ñ®9yV†Ô—‰_ÃÙY&áÄÄÄÌË&a0†#AŒz†s^ë 6n,«$›fimÛq*ç,P~ΔnÈûŒ“?ðhtþ  Pxø¹Å‰ b|~¢g-c±F‘’}Œæœó½.í oä”"¦Ýl\F‹-g‹1›5*P›^ÑÆ^EЂÝûH¢¿åR¾ßü¨ŸKÖïþâ?ã ÑŒ‰¸“¬;1ž·Åq^§«Ó¹?ë>Ô--OÃæã[È›œ0×·³=Pz ¡i({;<6†‡ >üN‚üÜYêÁ†PdBa0$…‹ 6Ce)Õ¨ø !D(Ûþ™¼;eQCP§¨[Y#0 #0°eA >çDpÈ5Ž@¤vìs=?ÈGýHÔ~Vëÿ\ªHš#0Eláöð(@'ÎRˆªŒ“ÛóH#,>Ðp1)#AŒXBŒ¤ße}~]ÃÚ<éI#/~ÿ9Aÿm›‘}g4sæcâtPu)-Ki’©H˜–½î¢##,`ÆDX©#07ÈÔÎhÐ2€Ub]…°‚ƒô{k&)ªÅ±Yø)r<]^~¯—iÝbݵڊœbÎÆHJYç ¯AG}”?Q÷)ý2D»R† #,Òˆ÷kJ€<‡{] wXåaî{ìÕçûÅqÎÒH‰¿¤<Á#0gH¥T5T“óö«,0bØ–"‹h®"˜  ðÌóB(HOº}òå4Ê ©`DÍ]ŠEP7)¼9˜€w"‡¦Ú1€ˆÀaÁPX…ýºÈ d“DË>ƒ=”k$$KZŠ]ÕÈ+m‘EO«vö×Õv¯UˆÈ'+ËØ\šÄÊ ¦)˜ÿÈ£t)£P¸Å` £l:Øy#,€¡DD×i œø %9V°ï‹_6Uuø—ÑH‘š #,=GPK(?m>˜I“žâ C¯q)Ð@2–1ËK+ 1“¼(Õôå]å\îǤP·.<rÓù_0DQÓ ÁÁ< `.{®tQ‹ÄA]Ð* bH£é2Ä0UzáU_Ì[UV-UFµšZ¶â!‰?¢#0#8˜ò~€ÀÏÐ’zmÊÜ- ŒÁQ£÷8ÉG¾ª FÚZ¡d§æ#0ÊÛ\¼R[­@`ÖšF#/`8#,ØÒÈãuô(òT¦£?Ó5ì‹ÅzjRI÷»xõ­Êömì¹s0×wi$¼»¹r¹®?üóo7HçGussIY]Ö9$úçY/9¾u¯KCéR§ûsâ@˜5" p¯ò„b*ÁXÈ0„‘,XCàõ¯h€‘PöÌP¸mý#/À(Ç? ÄDPV=CØù"{1'«Ôÿ£À­²¨¨®xìi¨Q*5‰Ã‰×s`wö´ÅŒ¶’>|Ü/ƒð&ºen@¥ƒ¯"ìe†XŽLõT0’Ï„Zô#0I`ø¼,DÒÙa¯üß"×Á>\&›¿Âž- ,@#,‘S8‰NØJÄPAƒ DŽß“CD™ãÄz}·ä%»#,Î%+ûJ¦GsVk+Ï*ù–ôfjdõwûRMGƒ%“‹Jp!LyW8Â&̆Mupö%$É #”h‰CKhgº•7¹˜ÒÂ’ã`,˜._•Š® HcÕQÚ›ø·Rï ;êÃÕGð\·ê#0­ý–Céþ3žÝ)è§è8Bk\†S\ý>fÖêOa÷xÄüÁa·q°‡ÛV«k¾öÁ .€½Á~²u輪k…ù.Ç5~ˆ¢{ß¼íÏ—ÇIµ´©åáv‡ëŽB¹2Ûˆ:Ÿö!ݶlj¸|ñ©ò¯-·É‚Ö gPgóÌÄ°í³’Ìý)ßh6Äæ²¢úOÂ#0ü}?*! U*¥}¼÷'3½e(&ÄóÒgKâÜQçAA !˜£ì’ ô 3!dE/FO˜ê©³L:²‹ªUâ[#,:mÐQŒ…†ÈVŸU•"ƒî¿ž}š\»LÍÆÎV,Aw†ìwVÐîÏáí:ÊþøÙeT¨ÂF¨¦I]ð&àïêJ¢Ÿ„nÛ›V-lÚ4ik%J$¶+›JÕ˜Ólš•±Tš¬4¨Ð¦f­HTˆ*yNèœmò•'(pÔWÀMñRDD‚##"$ˆ/G´dFȾXR#/€@&g{üûA^…¿†®,v¸dRƒªÇUÌ2`<42ÅQÎ$ÌŒhXBÓ1¸?¦ Ú#0²ê†ñT1X“XÈÔá¤7×\øD1í­ùñH«DCX%ìbÙ¢mâi´sw†<Xnhi(È°QùÕ†dŘd)J»*Ž¢®šP+Î#,è#0îŽÍ2Œ?Ñ}ÇÖH4·Å­ƒuÅHòŒ1`½2›zïpáÕàð/€ç( ¢T¦Ð]6î¦2„ !–Ð ï§Q_µc›5,¶üsTi)š­Í¹²W-vi›e¤ÁQQE´VåÄ6ÆÞÜ¡ñ•|d‰ág ”7Æ2"°$‚È9*=¢  …´àùt­¯ û=¸[…4ѶZC „M}è¯GF…„ô=CyŽá:Äí»SóÁÚHx|!€ä{ä=wwü~ÿ«÷þ¿›ý_ÛÇ‹Ì“æ{qB! ð!gw™~#/ç½{.iÜÀ«–½jü-Ó[_“éB0cñ4B!E0*!…_5S™ç6á{î;|ùXÅ\ÜšD´#,Ö”]`?”šýyYE>–/° sŒ¬WñÀè›ð Im»¹«¸ta•Hw¥øÿU§ou{oŸaý å$üÔï`i7V ¨eý½Ã_•¡hzùåáÃ1ÈðNÆÝä)Ì1#/·„—ƒ…8/£ ÃPÔ÷X ’§Þ¶IÉf@iœ3Ǥá5Ð6@D@hÒ:#4›z^IÒ|:ŽŽú;Ž»ŠXŽs€†(Ǹ®Òv•a½™ß©c÷éA”ýòe€;\F^þ§G§Ò{7¿„'xC±©ß›sƒê¡ßl(÷Ý°ý=‡X(iòpø¼8Vç«ì˪¢ðxì–¤aòwȇ°øÓ§„ÉXæÞ]ú9eåÏÙ]ôâCcØôØòÓ07Ý¿Ì—*üÿÍ'"¾à#,ÿ\Auëê’0$¦€{-¸PÀ1f ï;µ*Œ¦Jgâ”.(O¶W7¬9õdÖö' -¾Chy•¦©8U*“Ñ ÈgÅtÝÙŒâê˜!Ÿ#"# iä=Ò&8·ÄÆР⢶î±aýHxa#,`ö•Hø‡à ÚûA\òCÝJy !¯O#¢²·Ë÷þÿw¤¹W”þkáà2 g•TT*츈7D«ª>®‡ˆüÅ"0_8UQ*„€°F#0,ïù¿ŽPÌå„5*+Ç$ ÕÔž~]yC#0qÊ‚Äol­«ö$<òAV1óš¾–•€Ø8xÈ‹{wپ̅ƒqs›#,÷s0äœ(EbóYʈ@Ï8|ì™:?¯Ÿ¢»c/RüL¤ Æ̓ìÅ¢ šLÛ®~ãÔ¡ÕÁá™Câ*C¶×®Ë Jê:•ô†^œ1º´Æ)a¸\·+Çd¿®ü|›¶“{Vç2= ne>|ÏEã|sæðNQ‡›a…6³U()8¬jÅs¢#0Û´síx˜v$U¥ƒˆ;Ö°:<=v¶ã§jìºxã¬v×<®}ä¾ÙäÇBËÝöiu¶J„Ø@—(íÃíŸç­t ãyéµ¥G^=ݦ,ì»(É»Yé}E׿<{c*[#/Ï8æHˆ9x–ÁÅôÜëg^M³µ\9æ>;ú»‚­›ý1lj­ß>ø#i"0 cµnÊ6–8ê8é0Ò§j-C4ÂH#Ô(¼X¬ÌÙ¡·ŒVÍMÒê<ÊÄ0(ô4|>œØué¼úî1TFÞM˱48X5í­Mºvô\˳$7w4å9xQB#Û×€ž‚xü_J&>sà|}iSñsØp—RˆÞ˜U!QJWç¸]h~}§>dÚªDp3G€¥_¡w?pÄÅ ;Æ—€/s €#8ë#/}×ïdd°{]Yáp0áàBB×#/œ0]¶|¬‡çNˆž‹æ9Ýþ7236›pî7Ëä“>úñ® Z._>6ÛÑÝIÖa@_”"ÍÞ~!€šãÖg~ÇÏí|ÄÈ67n½ Í€ô½¢ƒnlCr#T™,¶À¹ód½™òKN6C†¼+ÑÁVøG=t¹P¹¢tðä9®ÓSîuì  Ëcžç!QÄwg#04p‘ׂ-UJ ŒF½|N[b ŒK3E/ÆïÂêÕ(䲩TõÖ'·ÙòÔô”PŽýšXp0 @joÚÝÈêÃá\!ƒ ±L‰Ž1‚¯Ò[!då×®Lf›ùõÏyŽ›45ÀûX—avxN¿6œy#/…#0‚‰ýÝ €­zªK”,^çñ`ÀèÔŒÈDƒÇ*¥huI!õ4{ꦤoÙåìÈó3Ô'#¦ût¾K¨ý8˜õN– /Ù›¶Ë'z)š¬©©Å?w§]ªÓåÃWVåç•D‚ùƒª^ÌZÙ¹@Ñ9Ýß@󢱘7¦ú¼¯/;«M6¦¶º×1K#0²ZºmÙ;«©f‰£m#/ ÂÆ)„„YA ®XŠÆÔ,U§l¨!¡J"(†—SËܱbDp\FÊͅ÷\pº]Rá"°H\&Ó8&yAH+$PcÀ‘$¯á;ÞŸyr(Ú›9u#à±dÏ(7]¢Ä軑åmJÛ¦Ť‚„RH"ü]t¤Y€m%ùUÓfMfKäBÉ„$žûÏu•@D¤…ptœ tß?ÝGÊﹺdê®ìÈ©Y–ËÉŠQžTê °¤“vÖ Ó%šy¤ÑíJ·vÚÑH§„ƒ#0×`„ã©jÛ Ão©E=_ªtÂ8ë¶ûÖ½¨#/4»v ™Al¯Ù…\ éÚyD´Åìªý.ÃÏ—=8í<v‰ÜÁ9!>Xò;å|Ôè« wJÀmeP#0aäJ"ˆR"±”ŒŠ#NFâȆ"ZÉHAl¥ –…Ÿv’¹Ð¥&`ž:Ÿ*³Æ`…ð+]{_†û¦V4[ºíVæ«•¢†!ò(¶l2—SFàaiõäÓÙÄs^\òHLwð:󯪰´½)³ä³ö·U^,›-ºâÛ=‚È„…ôx2 Zí›p$µßžAæ‰ÝnL´´Çiª.5mõžá¸i#/ÁŒ$ž{vºk9äòÿ=dËáâYt¼îáØ­´NÂ0ª¨5! ±JXòˆó Ù"â]-dÙHÅÌ3B¤q Üɹ¡Vù‚—‰øWâÔÌ1­“ °ªd…ϳ»áçð:ûã#0’Ý°ÛTzO—ocÏeW˜#/ý;ä£kz(W#/¾|r6I4M¬XŠ ;„E“™ç„éUçkWÊåÕS9¦…ÊßN<ÅQ\t˾Ó.ù2pIä4á‡Ì&÷F>Ãs)Ë…É$œå­Rצãßgµ9zVÜNBÝФƒ Cîn(Þ»o=_I¶Þ@ûyœ»—8ƒw”$}ù+HØ_šk`#/N`œ;>8’P¸sáÏ|¦±d7ýºõÖ:ÃC°ÈrÁÏÕA aUX¾aôëêi®Ú¼#,ã]‘kpÐBm.Á²‹Ç‰!hl.¡#`×ç• bHÂbæiÃÀ1 AH#”8"#s¹,Ô#/FAè@P€×EiЗ9xT Y"q››Ý”¯ÙŸ³<Ò ‰ª±#üØÁ#,ÂLÝB¥E@¨ŠÔ]%d€¦Àx0ZÔ à2Ѧ ÆØ‘`ŒBˆFµ€W#/#/0dL0Pqg™¥­ÂyD»°¼pÆHRÒ_BAãipÖé}<¼ÞÓouku#I¨†RE`MÊ#0$…YF£ ]JXaé²Ý„6ýÛ—·'¥¯='ªpJûiLx2HSÜɸœ#/ab:›õ|`*1qîQ|™FÝœlTM`_KXÓ7ÐlŠéc#,Ú§¬(¢äá«Ÿ)ô1AŸIäÃ&¡IH£:m°{û:àG©Áø’_¼–²_¸4q×Y7?9¤9#,³É% ߊÁ²«¡OÓêÓ0°Éf9ÛÙááêíGÒ. ßÉàfiÁ,œ/á]6öt¬DQ§.¦Ä©;–?gO9ƒ¤x‡#00Zoªë´ ºµºßyùÿ%þ–ƒlT˜:)Òªt©Zžï/§c¾ûCvg̵ëRÇ®tO¯LYßé6àO#0=}3r¥z "riqJ\Tc" ú b“­ì½ëó¯‚óÛ™ÌV£Îÿ¾'Ɔ’S Õ…Â1$1#/Œ2Æ#0'à”ªEaŠ¨f8bÂí]eÆÅP*˜Æ†$´Q€Q¶)zh†-Õ.»ûìõ2…ÅbT«aÙ€—ˆg˜#±#½àsvs:ù7‹ÌbÕ ªö«xæV‡‚ÄnÚ}#0­[U¡1à#¾† P®wzAú#/uƒ›ôÅÃA#,p&9éc`º`Äfaú#/Àn#0†è¾³#,#,ÂwýmUM…&º¨#,â<&;.«V›T³ s=λ&¾Ë™¸³¾Ž§-²n±Þ¾‘ÈÚØm¬£^2Y–d˜•ûòmŒUÈ’ ¼1fsä€îô¾ŸÂû}õ >)š(ô·­èˆÿ$<ñS<#0îˆeß{½Ï+1ª 4Í]6Ú²‘6;#9#09V#/6)¤þ›1ôj2¢‘RAÒ‡ÛÛg†qÁÃ7C¦z5¶¼µ”® ¯>¥ f¢Q•z‘2t¡,j´¿s#$ˆ7j0bhJ,„@YPb`¤1šÌo@¾Ì}Ó‚€late<€µªT=êÆƘ,GwR6º9bKÍ¢WÖkŽGž+øï4ÖîvtËu b¸S Õ õݸ%…nFÏUO­Ð”¢V¬Î|³–Û÷6Û\¿JyR Ußb —Ž7‰Ê:çlRëÈãBBU%;ÊÏ t.š8þ–¿¥®C8„ñóèˆäÚņ˜†ÍhKÅ,Wq6Øi‰ H¢S‰Q"!pª¡£}.ËÄ´ß,¸JÊ9ðQA¤FÀo€œ1l®¾¦”1%G¹‘M±2%Ë;I²•œ[Âh“t5wB ŒEÀÁrÅFЄS@û0ïFÊHfÍí±Ì.šäq€ô2&HD3c %¡‘#0a,VMPË{Ô¨Å"Ã&Éá`T²ÈfWøÁòMåµC?äsƒ|LÁ™iG+)¡$^·ÌfÈÖÙiW¶ ™Â¨'0ò‚1­t3»ñäwѦsÕ˜%Ò¹üèU80<B’`åt$%u“Ž…Ê"U³˜–c™@ƒÁµõºf4Øq¹ÌeVõ®7óåAÉf‰‘‰Ab#,Ƚ0M„Š;|(#,×¼¥54("qïJM=4)ƶˌÚy™ÍÿGÇeAÕÆ>©†ÿŸ2ùĺVõ2ÐL¢*[¼0 vNéxXß²uÝ]0’m·ÇR«cÕs³d†Mé×)†—Ÿ*Ïq–£/Ê‘pÔÏ—“ï|8ìl £à~dæ´O/±ìß–Ú¥nem[£RaÝÁÃL¶ÈÀ|™í#±¾IéZ‚\‹‰,#/G^À› |¡ïGñ^‚‹Ð†:þ|cÌW‡>>v °#0@Ïî5÷ùúIÚ–¦–Ù°±ÖÑC .”]°õ‘`ç‚Г÷{)d^õ€ :,ÃK*ŒI¨’&&›:d’9€•„LŠ­€Œ\lb±¾Å°ir€4#,ÞxsL:mDX5À‚4áA„#,„ h›ƒLTS#/Û97Gé"Ð^l#0Ïã’4‡¯ ýoE‚¨’tÿ{,ÆoÆîËù¡ÚŠ#, ¦Úž:•í¦×•žhþ<›¦{“Ùª”ÞKÃy{)‡9#sÑ&1¦ú¿¥AØÓ„ß¡€¾qmcQÓ?i¢š—•‹sšÏ“©þ¿\Añd‚0Áè|¬€ó%²§œ„>ÖEQ@H,–ýUÏÜI`m¢ÕFµâ¤V¢²H‚j(È¢’Ñ…#/©ñÐ{<ݦs"!†Ä](oDð_IÎô#,6¥” T‡ÖÈ×ÍØw·:šžãé:Á<EŒT#0ˆ)“)fÔÍ2JdÓ›bÒeAûN¤ÌÕ¤0bÓJ”2SbP¤Ö˜}½¹C&´˜²J%3k4)“)™#2#04"¨¦%)aöwQÆÈ¡H’)&K(#/Q™0Z*Th°E0Š3ɃZb›bÈL¢h2f`”¨ÌA¥ ˜¢¦ŠFùù}ûîUvvìwR#â›hºÈ=ÏÆb'¡YG_¯óí¡°Ÿ¬¨‡‘¼!hÕ#P¨ä_‘ÃÊb«"Ã3Ò£§,d0u8’[@–õï86¦,PÑé,?Ï(`"°ßS·Àà{л͢ˆH5÷Ðy?}MÛ™» …­ÜnWÅÈÁg-Ú½w5ÎÐ’¬L ZS÷K^†^¸¡9õÌÆÌŸ´–.xø…öܼ8iÐmm†8`a[XÇH’Xl#/þHUîÛð×ê,LjQ¤†ÊÉX²QŠM¦Y€HÉhs0w¼¨·…R÷paD1šÊi÷4ãŸY‡23áâ«,><ÅpŒRS$¯–9m5RE2-œ»õÀ¿Ó\6·Ê^ÇH¢òz>"‘þÇÚCº‘–¹'§ièêO¡Þ¿‰!ã<Ùì9† —#0¤:j‹ ®¾GpbÐg+Ïð³rê›Ó.¼Ì©¹jK˜»q>-ö#×™²ˆBNË#/ lªF©H´H#0’2$æ0êÞ>E#,”ç 6ª½nʵUyu¹ÕÏ9Ä8…›RuŠ®ïÇDÄb;Æå·ßÍçl;ôw‡b¥–w³ãÏùØ„ ’1,-“P½QA#,îñ®ÛI½¨~öA‡³ËÆžMx¸3ŸTöE7>sË3ì8qÒó\^ßfžúméÇ¥×™r5†7äßiƒ¯K Óì˜sÄxßz9Á#/¹ý—±¬È°](%Ñe ã-tÖÙáYÖ²ƒ×+S*,¦å¤žOA¡7MDSŠ ‹Ìy%Ñ ð’mÄÚÂðk©FSW/ÊT§©òß5É{áÄé(Vôã„&Wá ‘•¤”?÷B`ì#/ó'#0ª.ÐÊx JOKdƒmw±Ñve_Z:o mȵ¡ðÛIj<ŽÃ¥³#,OÏé›ì ~žšO6Þ}ÍÙÝЯA­$P!ËwÐN²­óÑÐOMz;áÕ×cðô–0Ngq½ÿAÎÒt¤*He‡ݽCîòÈC¨ìÓëçñÞe©ÛOÏð ²´÷(–#bP¡Ø'-¸ÉæË©Ã/Ì´ñ*OoW…½ h•K/AQ%5˜zSCoá²ÚàUÙ¢¤¯£RÇh ¸AQÍÙÈÆÎ\6&D#/3ÉIÌŽ×q݃Ý<|‰ßPÌt0µv“—³Nˆ®€€Á °Jì ,ÀNeûÐdF6Ö½gÀwéPIZ¯[3#/å‹,C‡&™ò}Ñ‘Š<kR8df`Û3R°Óƒæ­Xåy$cz™šÔiû²Õ½Wô²<3l»áÌÌr^uqŠ¹¸e†ÙÈØíx,à âhÑ©¦U…—‡3KYªV¡¥¬30¶™:ä™…6µ3LÐiì»Íé«#0°ãôL͹ uämánò˜Žƒ†oLuó‚ý·]¸#,aÇݦù Á31š£óu¾!Z”ä‰bȺÖbèMlÌÆ´lÇX#/¬kMRÙëÔ¯ÃÁ»¼#1²ÞC´ô}N©²·EN Â/N8¸qÜw—©x3´aq±£Õí`‘™ÆÊ£LÀ±i¡'"êÁsIÊ–²Íuèb}î©Ñ~ÑdäL(ìeIo0@˜©~¢+CÖ†'wgm?f™7†ÕØÈ:óm¦¬Ü,èš«}FÊrF­Ô”Á:by7fë²4ˆ¸XêÌ…~¡¦&#/5ríægcªíµxÉ:ß\Y—Ó¨#O1ñÆvÆøƒ¸uÛfJ3­FñœäÒÇÏiùÌRa‡Yir}Ü›ÜÓñßn¼nÉøõʑ°v–vˆôëQ¥«¬Ž°X›e£u)ÔáSEdÉ6“QXÄ9çþ±Ú¹½o¨;íD<Æç:q^q8‰2Ëœíƒ/dRÖ/:¨ÖLâñŠêù©ßU°¤’g·f…‡m¶ÂªÜßlÞøŠ=5œg-÷ Ûbä‘Åž·’tõx’Ö4äNѨÇ7ÒY,mxHˆYëÄÇVÃeØâª*…?•m‡ÆøyG:»oX˜wy·É:}‡¸4ÛÌbŒÇ}Üm³»b4Pûb+ŠxzÝž#/Õ—¥)T³Êdl„²MLc•£U4™¡I„„ÖI7;-s•‘â vÀnØÅ´…3:fg¤#»B ÖR®–#/iDÎ#L‚Z×I’%ëô‚]XoŠ¨0(ô4ˆÕG-CšnÇâ †]Qå’zs¬;:ÌÎ|iHÈVôø‚Ý5qW‚°rûE옴Øs(Hd„„˳«‚#/™ê7ãÉáq¨w åâî>‰å—INuª48ßxuÁ¥æi[ÊIwcAyÊÇDÉàÍ&æ`¬½F:ÈU ‘ÂãyÞëº)’ånmellðqƒ(•Ã¸„rŽS¹Å?³NK!ÉMyêmTs†@S³Z!°Ï$>ÎЈȒ\ŽØ7è$h'ŒÊuÖ#/P\Ü@;ÂaÙ#0›zµw6U;"áÖº< ؼ‡e½åQxáÎaèHâfX'0û9LC£Zyè›$ÃÁµ;b˜ZsD†Ï¦—†Æ‡—®Z9ËÖr›bå©4Ê#0‹€É#¤¤¸êéƒÈ.¼ÁÔ×ja5_r›ãlÃ%tÜÌ,&–2#mÛíVî™ âÕQ#3›"@ìHn¯Tc‡{síT\À®#0œ#0ÖN%ãð§ŠÐã1M:I"æRЪÏÕoE2¸v¼nSÞàœ«uh”’xœMÑ›DUufîýzVK¬ÞW}J‹“(JÆÍX\'s¨ºÃ‹62ï,Kݸ͖j¡…`šåØt¥/K»#0µP¬«s&\Øõªcp}4vGC÷™5%oZo4crÙY©iW7¯«ãÏ\ï8w+ŽJ‚\¸y‚fTÄ[Õàã[>ƒ4RÂA欭E'}ªò|EáÑN׆*0¯Ê.i NÓÒŠnéKjh–ÅP0 ÄÜ*µMY©€Ë-M¸Ü·Ì´+£ÑIÃ$8ã57V#o‡§ŽRL©²±³8º×CX÷§#-š»Ê马ycÉUc2ÄòL ÝZ™­×]í–(Û2pÊ-4ÙYa» ¹òº¹³áTqÁz¬êÕoz‡Š6C\¸õŸ\ÄkÄ›­b³½,¥šŒ)‡ÙžÇ/˜"ÖnL“ÆJ&ŸH0ç{–LŽ›Qo’D¶WÄPgGÈÌF'êD^eKÔÀ䩲GB)À1A&¦jr°4ƒ´6Õi.§8Tqª2ÉΦ-R¾Ö¥Œ ÙÑJ÷Œy^3xôÆÏVrkOƒ'²qý,]—Xtm¹ËkI&Wãp«Ñ$Ò±6*ä:‹wœÅÕ#0’%;“¢æÍ6Q}†Nhµ”oº”¯KÇÇ#,ÒÛnºCG3G v^†’ØË!sYNM#0Ó#/°t·E0›$!²ø` ¸àÙHŽ´lbñ8c‡²!‹¸±NÝqß-’k?‚ŠÈ¬=¾šu ; D“Ðÿl-¾”R(E&1RšIYÕ…b£3ˆ=˜‰i‡ ^¿804z0§ƒÁãL™`¹6 ¶H°ÜiТÉHmT°PÇZË0`aBËk5lÙ…<šHŒ2Ì lWìÝ%²Q]¤¤‹5ÅM“ÍÈ™ÉA9¥ïStÂCF^•)#/Ð2É¢KG+„ÙLÊ]·ÀO–õÉ};?#ƒcS+‘úÁ…Ö^ºàRðYR-1¤ “4i98&@]L Î63‡¨Ï<˔ͮlpqAe2L ^žÄs´‹;‡<ðû¤dË펄ü¡á÷]Î’ÜJãMηieÆXÌë¡Œ¾Ï|ó[!ÍŽrÒ±-ÅÈ}#/Ô6ðChÈúM6éñ:²a%f” ã}¨Ùøïµ¾»éÊÈw°Ø.¤² °<+ 4ÔbeÁ Læêi£0ÙÖ3Œgq©Ç@Xzòa!‰­¸Å”iµ¶V º ʨªß±ê¹)Êd.ƒÑE³†V0…“–še6€¥Áâ3ŒñõÉPºvHŒÅ·¾ŠÆø\Ñ\«Æ«ÙµsFÞ¶ÁÆ‹`ØughƒÉŠµßA;O —c‘’T48Ô4m ¬ˆ¦IÃéögX[.UŽ±¥¬—üÅÁõi¤ÇÈÀ©ùo3 ©Œvî­¸D z$b_xá³ËÁ´8Ž¨ ¡↨lá-ˆ€Q™Ã6`Š¡Ç}äY2C¨Åf&—¬Gcy#,ÕV%ébí [Ê¥"!À74o¸Âc0“Âêor ”:î):C]MŠa|‘‡š(‚\¸'i©P±BàC'7#}p× %Æ‘)iN‚™PLÂP]¹SD ²e¸Ã%½ C0é#/ „(à›™›pÁ­T¡k@¹ ,¬×›z‹“h*ì°á6@ȸìÃ\ÁÌn`èÂÁ¢R9ˆ•‰c}í,OQÞÆa¶jCjpàÜÈ`4™‰v[HÎp¡RX)Ü:¡À¨>n쩵“1“5fª¿›-v´~­¥/úI"B¶"†Èh&öZ"mÔ¥0ŠzÒ ƒ_ëÙþ#,rWìñ{ˆí_e~M·êÝ®Š±‘ZX2¨q€b0IÖª ë€0¡3l–¥+õÑ#0’ *gP4MŒi1­æ€#/$°  .ÀH;dA~¿OR›C»:~¸X&|©Û*†XgÒƒãÎñ9l‘ñí81ü‘dsæŽVÅ>›¶¢DÐLÍio¸} ¶ Iõqxxn+¬F·²3qRÓ͹™0 ]&MfÒ´##0Öí³`‚Ù°@Àq³r‘ZœC^'`bÌ·«7B,èìÆúõ#,c=¤å±Á?ÔNéj^ƒ|&).P¤IŒw©Äÿv´ˆ.ÞÛ·N4v¹fk_&µ¤Þ@„B*ŒB†À¼<Ñ$€`°tùY zºS.ñyæPg@•¨-#0£LÐ4M(–™ù~_:$mpý< 7CÇêúB#0+·îÚ$H_Á£³+I•²’HˆšlD$M.nº\Xn—ö+ÍOê©îE­œ#0Þl<ð;TO7Pò'Æâaþ˜d7I)›AS€ ½€ñ?F¢ô#,Â1Žtƒ˜ûˆ¼xõXdâE«PQ€Ž_qì¤Z»J2ô–4?.2b]üê2.)þ™¹ÉúÛ ìÄÛýçÃàÃÃÓ‘ˆc=8dcœr¬›ôØ&šTšm乶/;µßSÏ4m®þY4º„²…ä|r ÍÁiM_fÁ®Xâ8Rh5S† £…NðlxkI›Ž)…ÊÒ“:E̸ÝLðýÓËfIE­ %Ø4w‘Ž@ÈE6Ó_/ˆX©jÚ –FòK™Aì£çª(„ZÆØ£Q¶¨ÖÆ0›¦eIPhÚÅ„¯¯è‚H…(ÜxÞ'Å7û O6•¢§ÓÜ(%ºÔ÷mëð\SÅx—GaÝvÓgx'´‰% ¾è±n6ˆ,–"$Dm0Á%à ’j;Ht¶Á‹p#/³#,ÉÀ<'‰ÙÇG@‚@‰ØôÑi<]¤þk²x˜gƒ´ æNiŠ²™C'éº-(©÷hU“#0ý#,î×xˆZN^<œ«!Y†IÓUÍA¬™d¸8ó©AoöœT€Æ,u¡Ežâqžµ«•t­õ­ì½[ÑfVñ¢ì˜Å³)K 6 i€WV— ÍŒXÀdsùÎ2‘5Ä´AX‡9½ð‹öÎŽÑIbi¡ã‚mÉ$ ÛIƒ#,Ÿ8eëk#/d{†f>Òz½·Æ)C<L‚ov¨·š^Bg›Ñ&Fݸâp6—.ÐÛ Ôq”/yJtÔÞŠV»:6Ó”@R'qpŠÔ^&Ž²BBüÀšÚÍ@8cÉœEÆ.5•"cH1¤6 cq‚éɈ®Í/Ó<~pr@<SÂ'iÝâSr1‰&Æ2°%"L)ˆ†Í0ÇÐI‚â]iü"ÚÈ!PXKÀáglrAƒLÎ),1ÇÛ#,3^^UV©iãås3\ë—SœÙªg 8‘µŒ¤ýr…ƒ2•÷T#/¦­@Óz›\ÍgSRÖ”Â5ÇÒ‡ÖóŸyJ6´a(:u}KÌí6ˆâðÊˆïŸ Jk#/Ùh°Ði‹7joEK… ¤ke1`ÖÑm-,í!-ŸëØ1aàsÓ‚%u^‰gUŒ÷8yׂ–œÙ`æ³2ÑX–1$¨V ±Z¹ª$§‚»º#ÌX+tûš²Ðea1„F?Ÿ)ÃÁúºÙœ¦7ì`‡wgFÏb †° À呇‘8ix7œ01•‚œÃb"3‘2´r²äMªžæ#0íK7†õÎwW#/pƒ4 ÏñqÖ=‘5:ÚmuË;Vôƒ]aÀb¶t‚%—.F0(£ݺº°Ä2±Ì°Ð¸#0,J‰UA*$›iA±€š€ÇHp…ˆ€@U,$˜;Ʀœne '¼Ù|ÐÄVú‚Þ(yöð#0—p{þ”ú¿ø³&}Êå4â‘E‰ì `õà­ÒƒÀøJÃæ?ìñIúaïTAÂZ»ìžw°\1£»@zdCï‰V«ÎzSBQªy‹ÇìdŠ&w]ÝÜ»–í÷ö¼WóÕQkFÖJ«­&­Wæ¶T´i%ù÷äÃí0}0|ö´ñOtçÅab\½ðŸTT¡ëí?¢;ê—ÙÅñ;âY,,Õ¯ñƒo4Nº§xê´ok™Laʱ+‘CdÞΚpÖw™Ù4Ì™W52:2¥Kºye¬Êu…X¦Êl!²‚‡ÌÄ#0¥«; ŠéJ"hd6Ä0Y¬a Âò—³ÎfÜšááH Š9-Ö<Ì8à q©„¡•¨zyv§‚·étµÉ'ÒegypÔ›"&æK¡Àt)¢øš€v"ÞÄ£h†P–ŒJ†±W#/g¥sùÞi¶?z"#0ÞM©Ö⼉“¹‹x²Ónœ¤R–ŠSæ)#/ô3¡˜é:âLJ¸vFµPó:}¢Þ*¨i½ðxXÛz›Mp“\ÎJùš}2”æ¾Ò÷dÆtÙq’L•„3ä]s¸Ð&QI E ‡/J¥òtl›PÖÔ9¢ó3÷0°| P°]‚Œ#"h;‹Ûi©õ÷Ôó‹µcUvVU-Ê»EŒDqX-–QëËØiZe¥ˆ²-%!Ђ¸–…!q“]†V¼¼è£]n[iÖí¬ÓKkšœ’èR¥*8ÅŒ#,öÃ¥Yº’Ã#/óþš¨çÞ†˜eP1šÕ}1ò—uÛ[´.uŽÍÓlwuz“su2î:ë®'N¼¢kt¦›!·Z»I¬•Ývîâ“E¯;¥-;Ow]æZñR¸Í·+vš£H³)ÿ ñR$ppRŠm5 `4’·Œ¦´Ëd”™eclZš‘L×Sk¥ie-2kJeFmTiö__žQ«3A€Ñ³6µ¡*+h‘$„Þ[×ädò5p¶„Z= ‡S☗ IˆvXh"ð#,/yÁ´`¦ J"Rª×Tyf4§/š ¼ˆyÎã×`ôe„ôŒ;^½?C#0XwrØe;t#/#/}†ïža»£h'¼ˆàî;ø½Ñ~\?aWêG/ñìÞz†Ú=šòÒÆÀÔÃà]#,"Fƒo“2TÔ±‡›U¦ÚqÍ1Tïnÿ3[œÙaÀqoRja@œGë [Á—¤I¯å‰Z1! ÂåÓxÄ32¤*!h$Xpàr£ñsXïÈ©#/X™»3=ÚX·×“™ÆÚ~0#/šD°*V9Sdvf$¸Tƒã\-je2+á¹@À@ŒWM›ý_¹×–Nn¬;Hð#/MØp2Ð>uz.<ºtu?¹“˜ü¤òÁ½öà¼,Ñz"å#,@¬\q¼'T?w…<EÖP-ñDì€!“#,M˜¥®^–¨ù9†Ä%Ú°g¸>Å šŽ ‚낲œ Øk1!#/¥Š¯î±€U°Üñ5·Ól-Oºª§ q)r œšoîä¯ë#,€H€@ˆ/‡£äBºaõs67ˆ› Tîµ”~Q€ôã:”pùÿÐÇçab¦…•X@/Ï6Ö¼KÞ®›cj""‹hÓ4J“ÞÝTD’Mã[\Ðf¢VIŠÞn«\šm6‰Ki6b©4Ë&šd¥c1K5 Leø›¬3jÊd6jbKEj–Õ4^÷m-²»ºZ„ûÕ»kµ|vìQS‚fUi’ZšV¤«jhÅM·ñ–Ûªû…F’l‚¬lšÖÛ$‘6Ëm¯•Ú–ªI©kyÞ¤–›M”fTÕ†Õ–Ê߶Û-±ˆÑ-Ñ´Ùo:ºó¹%-S+•Õš¤Þ ¬·uFLKm ­xMqŠÍµ¦ÿB$+Ø¥á±Éò›v†4ɺsÕCþó&¨מ§Ûôž-{wðáЊñ7ªÈVl+†¶I„6é×ëÝâ5±ùýÞΪµX™¤ê¡=)-rœä¸rÅFÊhÍ­o›m>{¦jZZYš[JA€p²éõÑ$’h’"¡Y¥l¥[á¶Û¦­~fÓKÍÚå5so³º×”ªéQTȵDdAJß@­„€š‡J¢gMtÚP¢b¤E4#QdJ´¶j6b–32Z¾ëJèÂQ©IjSmZm”ËY6°Òš)R›}kr†ŒI-F³lKA1J4¥£Ci¶RšDÔ™¢Êeš-ˆÂŒ­”RÆÉ‘,#06J²UJV#0Š”ÙRQ*“j)6ÈbÉeTjͦ•!BJLX)%&L“L”RÍTÛÕU‘#j)bel˜E•­K5“M)’Ò–Ùf´Šd€•B Š$Rbˆi-ô•ªé³fµJU´–Ú¢ÕòÕæÍQmæj±khªRØ/…UÍmH•jTÛD@ÐVh»²p^wõ ˆuœóx³ 8¶½°†ZäïÙ2í›0ñÐ#,CBTîØ?Ë8ã(š9ʲHˆž#/ÛBÏi%Iè<Á¡åâÒœ \àyvTµD‡Ë+÷ˆ¬Çh(û¢yþÜ0D7ÇXõBC îÐÐ(ºŒWçååá»6¶íÃÍ«k(&cÒ]ÛM —C’”nÒ‡ƒÈ"¥+´Äº¯’Z÷/.Üÿ+€Ñ‡¿7×½™C¥#0ý¦ÃE‘@Y q¡„†ò<€§1Ìdq¡1‘5,Ubª3C> Ñí#,õÈ×ò°O$ tPXX äl5ÙºŽw­Cl«<y5È?VJØ¢)oÏàP‚TñÏT8G~D0 ‹Œ¥–VŸ÷“ï~fÅ)Š˜=Q(¡ÙÇÍ–7mòCÏJ°•C|v&™x‡Xœƒ^[±Û}Pîðsø/qX‡:>ù)grãX°þ/—<¹{ô.×B¤Ì0(¾Š‡ÉRÀ`ûèÑ-iúå[¨c¥ öíg#/‹ ¶ðJÎpnä)'›QE«Æ䇖ü#/±ê¸7Tq4ošÍ`™!³#/”TІ†eíKçw”#av‘ëX…üŽ—Öù÷mñÚpb¤]À#,±¶ÆªŒ)'#0¢€¤WJå œÉÐMÚTAöƒHÐý¾¨.¡æu´ž¹=Y)*`" ñ}9ô>h " dØñ)G³Q¦Ý!p: ëèa®Ñ=ï»éHÙmzV§Ðfoƒ ”üz5Ûí#,òÌ8—Mþ/wbèpê󺯭ÀóWW|6Ä0:Ìoì#/¢ó #,äÛÊÖs®™À[§øþç#0«UuÝt¨Š+›‘~Ǿy'ð<øvæ½X)WtÑçwË~ûzæøI2ÉBJ1­Ë#†b‚£Y-ÈÀ¸&*«¸XàPDGFØ€ÚƒN$âÆŸÓ OM:â¬A«„I‡9hå‘`Û¶ÒB3› í{•rÞ/ûÞKÞq„Þ5çŠ%UdVº]!0`©jÐðH°Â¸ûè)#/ñJ·è©5b0k ±Òôï £Âg+Ѥ£Á$}ê›leÒб™èÈì#,q#/cÞ†ýõA™ˆa+BcMTL,UŠ¸Xí¹ô@M°HbІ}±çÏV¦8)tú¯6“°WdÍ‚×gj“‡&½rÉ/Óá5),©F`‚Ø’a€¾™¡ÕÒ_vÀþ­ªÚØÎéb$=àüþMÁ‹:hƒábç ß·.« :¾_Hu‡Ï:Ë©"ÊcöM(pü& Åâ*ø>iB—+®4ÕS|òó›B½6Ô[ÅÓ#/ …ˆ£€“›4ÞjËÁ-'Á™óØÅ©I*"Êß^+ÈþªÇ<ø}ø{÷1á3zðé÷=1è÷ß)§³«ˆ?®L«Ïâe9š³ò‰.­'`-1o¬K+@ýÁ‚nõôã~ă–"aT’KJI>¦ËÅ0#ó!{¾éµlMi‘¦”‚&¢Ê#/#/4’v$,@ð’´`ÓTpÚ=ºˆ¬††á5BÇ ¨Á“(£Ñç{A´Aƒ×:ö}™›,;il™¤aPª °kºxDr,×l£ÌÖªÀ`ÚppÄÏF½5ƒ¯¦§&h†<+áÜÃò¢¨H 4'd#)ú)4jÞÏ Œ|˜@ª‚'SÑÄ0è¦Õ,•T½G$°NO¼P)ã6åç3Ú¦|ÈB`0B"ðl«¨[ÙB¬SIDÐã¤Åõ 2æcåÝÈߧŸ·µißôög)í[£CõÕIÏš<<Ó‡¤À0¤ ‚„1ð4‘DÒ$zÑa\ž¾*ÄÖ-?gÂó¥´ñi…¡Ï~k5£Qº™4Cé¡«a4¤Rbw^ÚaA¨mw{2´K»éÞÃmeEÔˆÙNÈ™Últ÷e±a°6›ä°j&@È0èþ5$åÍUŠúh¥^Qz‹#,ò==Ç îë7•³<Õ =ä1SÑGßS™ÐTX²…„¦1€‘A‚k¡Áµv„#´#/4µT²à€Ç,fDh¥7-çËûÀ]ÛI9£¼^ƒiJtK#0HТqAß©b È©#$ʪ1›Ýn»î—¾mu®ï£œ¸ø´á†7½ÇÍw&MçuÝÞæø4ø÷‘¼Õ>­÷5&7›"lQEU"¨ª¨lÅ OMj%°4JH©¥S®¤Ù˜b…9·gѾ|×{Ü’„LÝÚuÂBŽ<ëÁÕصÉM.yçZÇŒþ{‘$òº»”ªqÈ7–m«ƒs"ˆcLgYgîˆÓ-LSHÆÐÚ¿ZDV¯‰…&/GÉë]ìô¼9Ij0<P_"*C»b».a–åì{De¶Ãv=ӑßeg¨é½„å´?}RêTvÇ=ׄ¸Ñ^7Ü<2¶ÌŽNDŽé%‹¸‹Åôu¦ÕÁ44‚pÛæôeÔ%T —Ä=—ÜNi;ž‘MAéÞÓZã! õb‰¼õa”cå–q[€F"„»ŽŽ¤æQL#/#0¤ÅSYUŒ'oš0ƒi4{}*¡ƒ.'èÒ¹õ<,UIiïmEÝ×dÛð®È™”HNóÊøÕ{«©vÖÑÕ•$0ŠH¤/jŠ9«Ò†ºº™*åÌRøL¼Ê¯4Ýyjam–Y@21@xðí®&&`ÀëIJ›zòØ»8<fCæúöñÎ  ÅCÁ#/#/,_O„çØÎåK5Âwc"Zë@Ä'Ïs%_¯=^ÙfAfÎëj(MnBÕúZÓ£F¬‹–ÿC#,°VÚ“#56BL (MY1B(¢µW#6Ë4šl®º.T3ºé¥E¶6ß‘¶äEˆ­¯iF¶õz½›Zñ¹ZY·÷_Íï²V´Û*¶—©.¾%Ã.Ía;_»æ…™O·XLºífq!” H,€¥ÆP 2Ó‰Œ1Ï,X1O„ R9<ïp¹‚ÓÐbã! ˆþ·Ú¢ë\ãÁE±‘A#0‰>„øqçÈÙÆÎ:.XGÕ½–#0ÁÞn37œG×% ÄOñˆ#¿`´¥¬9o¢¡é$6Øj¨ü•J£-U=oal Ì1)$À«Ø3ŠæH<ö8}ÞTbŸ«mU5æÓMw]±i‘¨ÒV6ʼnJ @ °ÝëÝ+Äs0F¸HÙBòѬp0½…Ü2囑î¨p8m芀Dâ†VQ-åolÞzBmÚMò€Z#0’Ái)"'2Å¥<ƒ$ü[x c,¼0úÿéà+€‹µÏm&¡€m P¿y· J1#,¢€”®&€ßöÛq7õÙö[ºXÌ<ñâ¨Ë#/ôQBN"A@'äí4÷k²&&’‚_G߉æOŽ“ìËu­ýÎPR=¡:™Ò@@ÎQ+#’ pf-Ï\Û÷4'šè°å#œexóS¼+ÁEZArO׿ÖG **§!ªÀâC­¯%v•]'.pñtQñµ=ˆ‘o· ¯àÁ1!ý:'3U2ß#0jŠlAåáâ2ÇÞ¯Ñ@zý& q¿8‡˜‚"AX­¯Å5m;µ¹«jém¶,, TT¨ª¤€„2Gz6,À¼¢„‹ ÁdE¦¡èÇ8×æDè[o÷¡ƒmú˜3%t`¬Q`fD(£ ïÒ:LNÓšs>«§¬Ç÷mZ©Â¤MòRHô$›“Ξ´£G¤'$âlG•œJÇúDáÌ:$’‚’q vÙÚ[!Üaµ%ªE–‘\4¯!$1î;¾ŽÛ<Ô™¬Ò¨ÄtÓÓç¿!¡f‚^‡)e‡¬Ð /Áäz‚íó¥µî=uÎ6Ù·¼ #¬h Ú¯qve¦#0BŸŸeã”?‚Æâ*f, §5Hÿ=fw¯WJ‚ J~¸U V ½H§õáŒ?sZhÞßë/Ù­L1Ò¤X¨ªP3û®ÜSEC(y*˜”°–; aY‘€RŠ1dQÖ¿ágîf„âÓb˜A¤³–.ö 6±Šv£®Xt°KV,s|pa‘d#/˜ÁIx(”˜@¦mÇ{Ñu¡CD)’[ pBÑB<)iŠ¸¬Òc¤ÕYb5{ײ¤½ñÕ‹yÍÜøzãFÒ¨Z‚)"²iÙR‚’+h¥ “F˜-ëA„1.©fjC<(†bEjH2&´X@ E˜Rã…(d†˜‹!g4á‚ŒŒá† ™LŽŒ´µ‚ÙAiÔ£tʹ¬ÒåædU…aŒ9aZ,M꡽dXeƒ‘iªÎ²cM*1±´Ú£Ž2-‹•¢›»b湨¬k˜“óç½^™Ýv·ÔÈ[¤®DFq&tZg#/Zu‚­Â$¥ÔoüúÌ4Ë o‡á‘Í@¦²£¬ƒz}È®4MQIi¡tZ¥)ÆTª„#0X«+ìTz§­#/³ˆ H«%‰w o}ØÐXeÚz#,2aÔ”d qüF´>@}4¶ÖO*“ ×bnt!cì| ò¶éŸ¾ÝªflÒE¬šÌ%¥"•ji,PÄ i‰tb}þŸÌ”9âù¡Ï#0Ü{:>(y²rDàˆ@TÐ*‚zCWöÓ>è4BÄ"‘«¢/H‘Zúÿ ß»+ZwžVÉ©³5UvÛ)Q&Mã\–Æ+\­·R±kÛnnÖÉi¥M|îšËYv«Å"hn¢¬@E‰Ø|ÓøÜ@§è&pTRÆ„BÀÿfã´û®E‡Oà‚$@ƒÂYóçÝ×ìïÆÓЛ,`nqú½FkŠžm$H*ÑT£?t(-«AUE#0Dœè°KƒMœ'MY44yóçö}œwÍ sÓIí}?ÏÞùyÍ6@ùMÌOôDÆ#/à^«–Š+oÜîø»v·­\._SÍ6EAµ‹+Q*+h' b^»Q±ªMƒZÃd6ØéXØ®«»b¯Z¾.÷È^õ»*ß[mv–ÑW›ºŠ,ÄÂ’kRV©3M’øùíá–×Ȫø`‡bIU±”YqÑjV?N]Fêh£ûÊpŸD¢ã}K&qVAe‚¿*FU&`=î 0™I˜6$Ë!2ÀYc*ÂÐ÷ÔTkÓ,œ`óqØý’–n!lPü¡³Æc£/FÕ‡±IÂ{Û$6H—}ÆG}:—ƒámèÏ/¨úø±ea5j©8#XÓ¿ßîèmëÚ‘}(œŽ ¨V;UL\.ÜöÐUH.#/‡Žüøž´}½ŒIPPøi , ­ð ú¹DÞà5sê#,J¡³‡Ná]@ì<U{ʼ׎uvÝÅ%SiJ˜ÖÑj½›v¾“]£Mk4ÖSâWQ´‰š+)"[s ¾N¾½(Ow·„…¦é¹4Ç9oŤH@ÂUÀÐfzöwØßPõzvΑ8B1Gä?îó°ýgµ͸˜—6m-A¤}ÑJÆ&ºÜÛzfs»?d«Âeçž4]o”DÞ¬†ûç}2'Mª÷’$F EÙh¢ ãx¸ Œš%ÈV²ž#,—,8…l¼IT6ÌGï]þÁðò{zÄXD’$“1: #0?¦71q=^o 7 0”9¼Îž©C@¨ g½)á È–! IÑŽ0H•!!Jˆ`¿¿Ý´6æp’lèè&þhoS<0gQÒUɳšb?uŽ6 ¬Dðãu©QÒÖ•EU+Oì4º]0«,Ù†„Œ×rbßFÚîm„b!‰ýŒ©&Å0Ù#/9$¹Š„s»#,ji0ÒÅHThže#,>¼uý˜g» Òz3C„3q]HeŠ>Ž(q±é–9k£_øÂÁ2÷eŒ0Ë™ú¿ƒ3 &NF¶¥ÚæÄ:¢\ëoi™ËfYžÒŒ¬eR|᱓«¨¥= ê}©¦kÑ\6¨}Í'B¶60BVZbØ‹e¤¤¤Ù¤Ö¦Öš¤ØÖŒŒfšü²ºM*-,ýÖÚæªfU6chÔc4ͤš³jbX­•l­6²”Õ“kÄZÅ¥lÚ™K-©¢J¥cVØÚHÄI¤Y™øÞj?*aýïÒÒqÝ€k-†i™¢ âªtÀ8~Ô¢ !«k©ÊÕFÔmQk©m«•»7ƒo0¨ÁoåjZõ­y±œ#,Ö='wW Ä8(.Ú5ˆÔB¥MÛ”c%Ô¿VÓ|Vߦ°›#,€qI?YŠ¹’|9P"‹&…Qɤ‡Å¢1”ú( 0üôûôè|Z'¿{ÊÊtÁp(ëÜ]´Rñ!YóU¿O/ÖçБWŒV@#0 5}q^àÌÓpž¤ä…Ÿâb9µû &#0"ôÅ-D¨5îr —µ-ÒãBú™â³•â [G¡àO~Xÿn{MÛv\ ½€Ž$S½”ÈõÚ“Y*Rša°[#0dÁÒÍÜQE¿¿ËÈÃ56?¥šdbãã½þ¥SÎDì#^F ‘|Qæu#/®Ó¢US`Ê&iï5.ï^OP;È;ˆ#,˜O›q,1„X&­É<yIc_pqƒd09 u>ßÉuaË?¬÷a<`B+X‚¬40’8FÖ$ÏçZ£«”QE‹‹Ä Þñ?G¸ì,ù¢t,{Pü?)qÀŠ~ËJϧebÜÂ!Bl´²? <ô×ÑUçЊ®}Iˆ†,•»‰ÇDê:bKõmÁw¦ºC]/#£€£ KßõëÃŽxÝ¿{ð^m<è„!ì#,å59צ_%vW¿A¼Éó¾§ÔI5`U4˜.È\c#0HB ùáb#/û}†óË™À} š¾(Å,‰ 5-úK¾gŒÌ_¿Â—‹Þ­èIÉïÿ`Àº5€ÉÀ}7s¼¼¼Ú²*6”²›õ;¨µÄí²8†˜Tì R¦›úþ¸ð‹ŒxÙ†"(Õ#0`¸Ò³tc#/©n0–€ ˜RÔÜ°˜I icÓQ‚z´öÜ73¡êê]s#/#/-–Ûƒg0†#,ÝræÙ-"ˆcMF#†4@Šxf£ûÊ#,›¦XC<¨Øˆ°au#,#[â#, çYŠ(£²}ÝGoË>?Õü6ºsÒ^´–%Êg#W Û:àuÞo\™P†FäjQ…Í ^êB „éi(SÙ»SˆF%D‰Až¥@¸Ó}×6ÕD ©ùˆ¼æ§nôh‚D‚˜@`!b…Œ!E¤¡àS@ѲY˜#0 ¤âÐYÀ¡©ÄP2‰èòð¥åpMŠŽC°ïñ2P1׫šž‡wg‰ºª…‹"Í Q‰#/#0¦KgcÃ-צ“ÖŸ•2í¢zÙB#/›v¸%¡&aYçƒmÂr¤W´#,ÅÉŽ»tÍå‘¡#,ë¹!äö$Í°Â,„EbZˆÈ1”ÔPa®Í¿£3¿ÂKï¶ß}Z(+ñ7J¥y»[b5¿KÊó>7Í ¹ã¤Þ\É) Ww“ªR"0qUK@„raÔÔ³•Ek\Hˆaî*…"­7k *#/¦â7¥ÚUŒß2Í0dÍÖÕµ­¤âqrcš¦\RA„%>â=‘SGÓ2Fا7fÓ½®Àe{•`3ëò;Úgk-·¼þß«ŒGa[»•X#/"£í¤_vUcŽ††Ú 14®\¨#‘ l6õ¥„æo!©€Ÿ$ݘ÷BþÙå¸â·HF˜…Îä§á¿Øç&±9/¾½øáuÌEP+S—"BLD­4KÎJÑ”€Ô9Y]ÝoDÆÁÕ&1- ¤æŸg§ClI8Õ##/»pm¦æ#,œ„9dfÒ““ªb®,+V8Ó!´(ÕçeäÎj@uåÅÉÑò³í$½%,J~ÌéÙF0Ûêð­N³–S©Ä¾ÞÛ‘>"ž5_¾­5w"cÙÍÓL|3’÷àÅ”žÙ>±ÕNÕ7/¿üÿäeryøúï"fœ³S°×b#0ÈĪ§‘‚Á%$L HªÝƒE!Kh6-€N bÍ¡æe[áÚPxéL‚žÒ+b*h‚ë¿Ó:þ´EyÍ»¨|;¿›CpV ^½6äð:´lt°PÄßÕé.`*HÈB'†%tîT!œ¯]€Ü²Ž‡+-ÒI Ä#/Tƒ¢ým#.Ž^:!–ݧVyPzÐû±‰°¾‡Ùæxì™bždOGw*±¼‚K1)¢ôä È–Òó·5ÊóΛ¯<¢Ý㶻CÎÖºÖMµRZÒËžJ#H%HTŠ2¨”c(A#0†~¯Ùì=†ÒŠ `b?á@íÝ[ø."#/6¤Ý%Ôh¢ª˜üeÝ×îð˜ Q ˆÅhŸ2V´ÒXÕbÖ¡CèÉžÂ×íë#0mÝí½u•ŒwEÂÓ.5[jÓëí\¤¯2ó7Š2m¯.w$F¢‘pÂaË#Z]³Z4À¤Æ˜["ÐÁ4!JRPP‰l7¢mpZi‰£–cÒ„Ö©‹³P#XCd W6¸Æ4òï)‡GDÞAFm‘µ¨#0)‘³'Ä/ñv­46i |=âÁŠ¨…€Äº"¯’hbÙlÀ0!”sÚ¾ˆûÎvÇÄÍ‘!…RH nA1ÔìÞyTñòû+,õ#/ÿÒ#/Cˆ—Wžº3ªaæó,ŽîX—ICLT€_Âe8bPQJe1+›´“©U==BP‡LàZ5_™¶®Í¦“e¤­™hÙ-&­Rš¯Ê¯Ömú­i2ºí¶wså·+•Íahˆ}]iØPoü{†?&ácÆå¢æîmª`xýËrˆ(°ò쵚²lÐ"œ:Ž<7f^Ìqà#0äSáôÏyGSGÀD¾—#õHC¡Í¯Jpƒ{¨òÎ!W–Ñm½Uàd’bŸ#/#/ýÉd[Aeæ÷O…†¤ðZßS5¸Úa$Ù6VˆIÊ^Åì‘'o׳.‚c†þó{¾äúÁ…_(›#,æ ´¥ê¢fHÜÄéÁ8aA—IJg£3ÒdˆÂEF1œ5×=9…eˆ˜’Ùå‚C*b’˜£FÀÓ"…ÃL;™*ÃA#, •·4ß¹ç@1mËDŒÎ(ð/lƒŽÇ*˜Î¯Q¸QÁ É”p® wXj‡j8õðKUØdéo#m „Ã#·Í㉠ÿGœ$Êe©{|ªUË1Ïg3·ÃÖwˆiç|Ç‚¨>Ì#03…Ç]§§·ë¸zâñ‰IØdÇâƒMÚls¦˜FœÅy‰MÅΟƛ¸lxÄbpÏ’6‰Ë,Íðئ£rêèáR³‘4tC“áB³š»sô˜ÂwÕD“Ú’g'áØã~s³Õ³“Ü„åÀ‡.Ô¿P”:ûÍCÂ!š¶#«)æÁ… €¢’Oրї£–ÿwlû·ÌÃs´Èg«|\eD±ËÇž×';~W{vßrÚÅöwF¼“ ’Ù…ñ2Y3U UT•"Äb#,Ñó™Á`-q÷ê|q–#,\`¸#ðìû õ`´—T"Åx-ª,,&†W–R´U`H0n¶Ñ;ŽŽL€i‚0A@‡Ô2à ˆ$±ÁÍ)PÃi¡ô^¹;ŽžžGN>¡93‹¦e•`v~Ÿï`cø7I«Kgd• x ûÀKóx#óð€„¶U†¼Xd›Ü\1–ý@P:J‚¦K4 瀉µD1Îë÷0¬Ó™£Êy:&xèZDzš<‡ApÜ\!%ªÖ¤·ˆo€sÑñ#,!¦<±D:‘¿µ¿DeŽßOÃjH*Ëùì44v #3^øŸsónBD†’EÑÚ£GÍ¢¯WUCní«®[—bÝ]e¬ÒÚi[E©IK3VV™µWU±£\¦^.I¥»jßdB$„`ÅŠ¨È¨²(';MO!ßÇäûZká(ŠcØ™m™8-€‘Ëõ¢l›cÕ|2ÉÃ=R‹5ix|.æsEÑX|l% ¯'!ÚA!}M¹Òa7JYU%D]3ÿ<Diôˆƒ#Hã"GQ›I†0å„hÕ‡åÈvêµgC)d,¥1§ÈkpŸÏ¹ô ÛG#/^m¼›v”bmÌLoØ‚êYÙÁî7)‹+OX6i^¦oöWY·`÷¢ PSöW˜ÀÔC/J1n‘âtzu[y¶Jíì`ááR D½¸é4Ôµ-Ì[¤è­[ß *Ä`†*W)[íP¤ü¹×I5É€\oVºˆf?jk›#ØßÖ_6#/ˆ$,Íûcû?Ùÿ?õÿ¯û›ö¬ ú”)Zr*5®m9¯e#±`‘‚’í‚|;i-R&ÑñÒ®­ÜÊJBˆÀµ¶&Œ‚¤?I¬½ -£«&ÐÔ»©ªe!^HÛz#/€5  Î1ص#,âS5Ä_,<NðÚÞèêcñ^&-‰ƒÞô6\ñB)$‡é|“Ž#’:oM[·"ªì¢ñù§üÂÏ'îÙI"äso¹âý·¬ïeRI%äÍ ¶eƒ¶$4Éöí×W³ƒù_¢ÈÖñp™³ ÍÕ˜wÉ„B÷Lo«#,{Ъö@æU‚<ž6i´ؽ¶#/Çîb0ˆÃ¼;àÎäºt…#0;tšB&à(y—IˆõÀ²RaF|‚fgRHŒç[å”°cÓ’â_Ìcs¿8¼hbi³àj¡òÈÿ­0¡”Ìòž‡OÐÔï #/C¦Ó=‹#/?£R3MSd·†ŸÊ¶+Gžõ+¡c(ãö¾ìi#/‰are†æ;v'\$™™Ôú¶uhøh-‡(¯JÀ»{hÛó±J&ëO³Œ!èàpbl+{­º5”‡By©žlù $|Ï 3r//–íóÕý„™I÷ ˆÈO¨‰™ÔzO^ñ1#,ùtì¸à'ªI-@…A(œfÇ‹Õ­¢¨Ûm%Qµ2Ú“kZ¼sj£mrÐ;ã ±ØheÝó®]+•F’äBZ’Å@(##0üØÊ«Ùƨ±ÆH1<Þ|OfŽ²ÓíL´1Ȥ†£ØŸ´’˜ö{zÌ ¨Ø¬óè-Ãê8á´ÁC/®ºÖA¢Ÿ…×qƒ8—6Ð#,ºhûF@H#0²£ÄJRA†¦ —žðÝÝi:9†<áÜPŸ˜|9©Ûÿ›ô°Ÿ¢ USF§_>ýÎ#,t…Bt(9˜ï#0Jðó©ãåšxiáÏè±i0ëöˆHÇô8ØD ÜÀæûÜæ'²ăöœ3Ž]ûi¾„b1¨zh{+qQ “Š„ƒs2ëG/Li1f­ìÁ´›*ìMSmÚQoöï{m¦ BoI8öCÞt>iO¨å•É³>ø#,F˜LžmÉ ð cß${ûªõàÎ7r/H™L ”iMAÙ†RZÈäKIn°@àò¤#/0/¤#²äAA‚èäHÙ‹"#0ùAZ#05+S`Pd$ncñØáέ³0.’ ;n#9†ðÄŒCdT`Œ,»”\n)õzüïK=Myz8ºéÏ3 CXšJ$J²DÖ m°bÄd&©¡=Ié¯^q‚ùbî8«e¹þ¾Ê;*d–Gu‡jùš˜ØZ¥„r‡—K%m,N³÷6#/äO3È¡ÝUÇ_—ÇÁ>^1çf%‹@ …ó?eÕqÀ±aMU<ˆ˜‹c¢µÓ?ѧ¡uPoòô„^Àêk#,L8¿]؇ óŽ W=AȃQ%ÍtÇƹoW¯>;å¼Zæ×5Ýq†#,ѨŽ@Œá”Ÿ¶´ø4=¨Þä:쮤SÆð«x»y7^[®¶uºfÚJÒ™›FŠ‘`,(ª #/Ê¿é‡#,ia1f4Á€Â€ª 1’XZ18a¡ÁQkPt Ž#/#ÁêÚ'ª(Ä8:5£*`ªj$H%UJˆÌ°*Ð>VPÈ ù*…ñ΢O4ÏÝ-‰DÑx#0E Æ1¤6,šéW7Û+¢Í“V’®tƒ,‰Å"L416„íhÊ2Ì›39APŒX8•#0*ÙE&h…×—ÇǺñ¶e0›œ­ež¤¯w•u¦Ê™Lh£R¶R±,3LZfÙ$™´Ø¥³2L5²–—½Û˪äÓ»œÛ’¢›®®UÊvë¾.¼kÍçxMŠ•C"å¾Ï{â|3a“LÔ<½/8ùíó¼øÛÌ”CU«à#/†«7L“­<Ù¬¹†J”0Û)ÄræëŠÚQ·I¤L5¨¥‰±Ü¨Š´p˜h5ó¨¯H²4®ØÐ÷´IòãÁ‰†{h·“`Ø@ÜSr0Ó\3 PÉ´7Ó¦SÖ9Ê~¼½·•ÕclÚ½µÒ,fßNéY©-}Ò|W“×9³2Éj±m¼âé%4#‘rÈZ#0Dbµ› ‘(Ák†Œ†@JJlÂV‘ˆR+†š…U´Ã‘‰ cxÛŒ ic$I˜Ùi-+Bˆ‚°Vês[`Æ6$ÝLhc3µ1†o×—8áaÆð1#/[¬¬ÂÛ2qŒÚ¡»±×#Ya ‰¥Z &LW=y¶²C$_[#Ó>ëL“aB4SÑÄ£Ö‚&ìÐôÏ.rá¿Ü‡š¸M…:USÑÄåmLÒ‰aãå1‹< VÅÙpªVÌš‰³-h¸i-<LEà X´Í··ª Ä¨¼1#0°0 ùdHøšç£A~óuÍ$ú˜i‡ t3õ_#,Ç¡ü=NZÈ=Œl€f˜Ð¶ôæ$™Lg5R¤…¿Á4+¸&[aXÞDFF(ÎX²C‚hÇŒò.(²dm†cfÙ%Š¥¦›ÖA5½oPš{ywí»BaÓZU‹f•1U—¬rØ©Bø-Mè1œmávßW¿¦³†=™h¡…#/Ы(~ËÒ­ÂR.°Ó:-1Q'*Vác(3*­Ûß›› Ûñ —(Š#,c J""1!¥ª‚4†Àƒ@šTŠƒ†#, 1(62ƒE!D°6HB*–(»’`—!$¸”_÷0"fþ=Gœ:£ëMM þ¼=_N“;µ%—UúÔˆ¿ßn¸—võ#T9†>²ÒÂõy‚¶©…ÆÅ\ï|Ó&M{kc¸{+åtª„Ùý)J‚0‚$žï›J‘Rš-!v&¤OíéååX®z懆´\¶Ã×mç³wS%,íîé;3…gž¨õgÑu—ZÍLlÌêÔRʳ\Y’œ<KÙåКVO>ºês_Èû«7u¹xr¢2ónN£fó¢ºç3naC-‡8ƒFbh¹,½cMK¡‹2Ž9Ø-¤ËÒèøk‘¶]W6ågC‡Áù§ÃvÜÎýýæ‡ ßž¢è ÑFœvb #ËÊ´ ÓF$·ì(¾‡Ùf'ÄÚØäõ”yÓÜý:³³¯DÜo¹¯..Ýš„’×5m¾0ó‚ž„Í7eES-#,¶Ùj¤3«žQÀ®ÌªýöRÛBp(­™ÎV¯o†´ˆ¸±³˜ï¢Ú¾ºpÊÐM/<p;²5S)ñPô‡œ"'Ÿê˜yÙqóÄ|ài¼XŠÆØJ¥·×_ÄÚVω•°´·³d¦2(&‘#ð¥UTƒFĹx7ËQâ”rI¤m!¡ùæñæ׎–7Š®Í[…¶å¶J®ñѱV5y×.šº¶å]Û™ ¹xÚŠu4–6‘¢‘•6ç#0ÛhÕ%eSzÛo¼dÚÒnš×mמxÅ¡6•bM 0#GÁ‘Ã"+ŽÌ«Ýb£¯L±‘IžÜ'3Ý„ÖE´’+H€ae¡QaH4ÅV kÏ$Fº—….øÙa˜”_;#0žt—7+M|my3D¶ö‚D-Gߦ5Âm¨ÀŽ$¦ë_#/ª#E­çv¥)ªIþcªŠl¢¶+ ™¥65ÆŠ™¢¢Ú+¦V "™Q²li”“1•MmLÉ`#,D.´D‡‘J@Ü{z}øŠFFIû¿‹ç]"HÍÝ÷µ«^üA¤¶Ñ’0’Ù}çNÛý“ìð¿w™éy-êþ^»—?¿ÍEÓ¸°x&z8m©6Ž1¯^ÓÍA#%k~³SM­%2´_ròSfÅUùf·âV·#/ZñµÒþ+uQLóuѪwvék»IM¶¹m£&±[º»JV´Ö·çÝ_nÍeEª»i·n»D¿í½?20&€|Š4ñ©'(1L˜Èeo2HŽòЃ±vàˆÚI #0 ™L£ƒjRÜuóàÑ)#0#/¥wáNbØH C"áÕ×O&‹ÆCbÚ8zÖÛµ=ãåƼóP<×(ÑL,‹ö>TVh1.Âê´a2"Àùf›’@úÎñ$#/ÜÕGïg#/k4‚èYŒÆcsñ²’AŒc+¹’i¨LcI0v™mM7Gša™&pæM¾ŽÓ)Ä¥µÞ0nÒÂ<ý›Î=~™ÈÆ6ºs+G@Èh‡C&;dé¶*ßÑ=˜ãTî°…#/l‡2}åt—#/˜U{FKB˜ù`¨¬dŠa>G;ಒsàÄòð€]œæj‹MÇbižl¬mƒÈ"Vz˜uÂjãL»½žçƒ¢ÁWøÉÛB±X|‰Ñ€°è:õÇnåd>—aºò¸Y6©²q²Ùùñ´ü|hUcÑ‚ rb1ñìšL약R¢‡ª2w¾d>‘v¨3 …8À~„#0­‹jM·×Kîj×JÒWöþÒ.7¥G‚Ø™AÄ%å‚Á °  „°¢ù{o9¯;]~ñà²ïè¢ã>-H€  _›ûÊCà΃õ&o0À¨B„"$ƒ#0)˜T‰"©*Ñ-ÄTñ(Á¨4(4c5¸ih%µh(hûžÌ1ˆ~0Ä0T"Ü«ŠÙ¨,d˜ÅÕ^,ÛÕî*¼î×`r•l…AQI˜YeS'kŒ%XÀ¹$c!d‰8 ÊƘÁhã1ºÆÙ”@F€Ó0m2ѶjÔ¥ˆÒÈXÀ¨&nŒà©!„À¢À`DÂ…",P¨Pµ±`4¼sˆl,.#/©±ó¥¤µ]¹ Ì@/\‡¨>û×Ò )#/1ˆ‚#0"0õÊy?_)P¼æð…í*K?A÷ažäEµNµ´ù‡k[×síI¥ÙªÜq±ÂùÇ †´É“*ØPçgðéŽrÀx=~ê¼Ö«kðʵ±[Wâ¼Ì!è@\U0‘B“­ïˤ÷9üãVHý”T1?óüÁã#/q¼8ŸT2…Ï›+¾3t~_ç#,‚Œâr™êÀÓj*‘*˜z*¬!»}o¹øËãN`Çá#J&}¿–ËWYÏ›‡ìPQÌÒø|S†œšîݹþV]ÂÙrñ™$b¢¶u¨:/p;b”47}Tên°ècëLô‡k‚€ohD%Xi¦Æ{Î)½°3Ÿˆzè-í{ª뚌?«wda!ˆkTTµ4–ÇœP[TR”<ŠV&ÄIV†ìªR[.-‹Þ¨—ƒ÷UÒ9å³”ìðëöu–‘û#»‰ãòšmzŸuM“pùC›Ý‘¶Íêј|H©Óœg *zTõ#,¢þ§ô0ú¬#,1íç¯oÒš.îuÍUËZîÖ[©nÕÍ(bý±µÑP»{Ô”!QjõÁ2{“ùŸ#,ClnT„Z *Jd„`2˜ D$ö›#,ð?&©ê"¤/~s»^.ï;¬Œ¹U”uÝâòœ·2óq´ÙdJI¨M)ª<j»Sl•Š)60xÝ#/6æÔgé¼Þ]¸ë®ÙŽt.Wwn‘\¼[Ç’¨å4·“ÉnÜ»šÌ±¡ÛͶò­m¢²Ucm)ciå¹K3IRVy×t—Nmݙҹ"•;®Ç*ë.u›FŽj¢×j[sÑî EÜØE.ª¾´õ@.˜& {6úwƒè²Àö»I³•Åå@äÞ§wrˆ#!ß@@„íb%€ãöøˆ‰Å:qQñýs”ö‚§`áz#0A¸ß‚Á^9>˜õøBzŠô_S_m¯„γáê!ç˸R}´ P~Ou·'åì9›N£Ø…‘Gt ©"1Œ’H$‘0i¦†r*MÄ€¹¤‚²È¨Z‚ïû‹–?¦#0Œ` œéöñ°(˜@KÙ5cUhRÐËF¾b!‰Ðz£}…!è#,HŽu#0@„¹Ú§]wu,¬XÄÝÖ»Û´FcmU64Á#/ÚWrÄq"‡¨¥@̆1 ÑZ•È„„$O{¦L’h6K嵫íëuËW϶ÛÚãʾ3ÕJ#d"Ò#/¡xªØ„€©E#JE›"Ø #0(i„H£#,ÃC¦{7ï\ÄÈ(O‚¢Vuæ…Oðâzƒò±HaJ¤b°J0=Çñ ÐõV¹gHhôEW„Ad"‘#,ƒ(1"#,$ $wxŽÝ~&7:ˆ)³1b°€AH0HÂ1DT$Š?1âÀ7¡°›ïß½õüê*ME¦XJµüâÚ–!a#,`& yÀ€õi³Âê=•Æl‹ªˆ‰)Ê#,ì¹B™«ýƒð;ƒÒ5$“hhm=>ÞwØŽ¯I¸éôw{¶îü÷Dúî$bˆlGü£ÌÏÅÿé#,¹/Úƒ,5ÁOåNA¬ Ð¥H «™!wD¿£kjråØ„B,”;;Žt½ñ{†1ô¸~Ñ°¿‡yåâšNÃÌÒTž8.Ãü#/4ø!k6$cC#,jH–:É-CJ‰Ç„"@ä@ØR&7Øä"åÆ `Að6Óal…0(F0#hM£{€¨ìZ„%D!#, „Xó›e46<ÇBP «ÚÅâÕÍŠ¹R[—Åš­x!$´IòulUW{²óŒc[É­™>#0Ò{^,wgeUƒpþ`¶´ $a˜ÃI!´¶Òàê<.ð#/Y$5¯Ú*2É“9¡Y ©Û¦`…©íã5VòUvÚ6¨«bµ[Û)TT nòb™ÄÍ#/qL±ä#,l)7nÜš… hã÷ØGòŽûµ#,¤óÍSƱè†÷è~EãxQ¤²D*¨ ¯Æ ¡XL(8*ÔHCZ‰ ¥¬gáåN¬7ýÐÉöÄâHÆ@Ô Èz =[ ÑPYT+#,‚©#/¨UÏÝè#0~("g‘•õE™+¯m;ek–«Ì·šÜFªB+NÆ$b‹HLЙԿ9넆ðÉ=iÒÝSzÊŸØHBÚý©øýí´s,¶´0Ú ´¬Òkù) ××*ùµµÄ_Wœ¿ZyÝz׺¥Ý+S\DVA¶Û’Y1ÁBHÓUѬx2<jf7d„T®þ¼#úz†Ù¥¡EX3“¹ÙÿUdàÙÀsP«/*­¶Õ{´ÅyÜè^‡…šg‚=Aš>"û'å±¼6™¡ä#žA?ƒ1>¨êÄ<e@#/³óUqÔèÞ#0&áËdD#,¤‚ˆOU#/0XúåHŠî«Q@éT{®f³‘s:loá#,p·Ú¡ÀvñMonøÃÑCÂwCî,UÒ#0™FŸ·­~ãZÉ÷·—äm&Hhü#,TÐ-ü¿Ñ!;íôÎe½E}Þº½eFŒ©_‰u%ƒhžî÷8~9,ÂUp^¼`ÀÏr(„£"o>lgsI9vIqÒè¾céƆm ðWùT/'a­½÷BKâê`¸@Ãu8ÄÆ^‘–MìŽò:ïÞU*¡É¤èîÍ‹Šá1sàÂb¬wE3‹®XˆÖ™Rú™u(2‚¸éR «†AdÃ6ŒH–Ÿ#0/¹;]ÆýÍŠ%ù$ü?9/Òv{ÈMI™˜©„=©#,ϯ^¡#0š¢àÜïdf#08®xTÏ\s,®hG)jpN[˜Z*ŒI&‡áÙª©ÍŽÙ%슇y§!#ø>ÊÓ[ÿ¢b˜û„YGjئt-ª9Á\Émgó2ߟ´´7£L€ÁA€ŒM+¾»ß:¼g.9׫—/”Ðv8€ ²LGtÄÉ‚æq¦!…“Lf&Š©­6w$îàÕÈhl0¹}ï]Ößߌi‡A‰6kQl €”õqïŽQÖ¦—¿ù^tžpÁo4—ÌìWÁî'to°s³Z Yù²väZ½»ñh%É iŸwMã.²º<ðme›¬2¿‡ÛÿUãÑ0™‚Ù6·\“!Ä‘Öä7}éf£uÑ¡˜„þѤA°^~Õ®· Ô=<·ŸóYK¼ezòv‹GMc’pC\"‘„hx•)ù›"ÀÞ˜a?šÆá2ðæyÐíòž²SdžF§¾Ég·Tã÷õÏçz¼ÒK¾“Ðõ¤ v9%PwO_—-µ§ô—;‘’p‚æD>ü´.Ççì#âh(kéÎ’‚¤Û)IŠøo<MÀß©&ÔDèÙþwÛa2ˆñÇÌ„öžtúH€ZŠ¨”˜A´ÄAÍvœÊ:wuñc€:ž9Úwô›—m­×êoÙœºSV+3T#î`#\{gRB’Iªýÿ°Ô#/Ÿ¨b#KöÞ1ëZ¸Qky6Kl—5s±iz¤‘œ[3WoF©Kö±úç¸}·4ݘQikZWȬ»õñëé­¼3¬¶',+ÿ/[ÔÖ%NEúrßÂ6+-c©šc50ZÙ±N›Ô‡líÖ8Ñ‘‘#,â¶â%>6öca96Ø5#0ƶ’"HL0!#0@Xy(ÁÓC2aE?=~¹a¼²>nå¦zæ«% åñ&饻¡ô¾ ð¥@Ó¹±£ÛÊ3Z.ZŒ!Ø„å~{w‰ÁU ™8¹ë‚EàœÁ‹)—~ÆØ5ß#/1P´ÔòX¨&õ½´ôæHI$š=»¼x_D‡nOì"öã~‘RŠMÎ#/(Bd Ý @ï˜LFñ§ßÕëV_*;@' çöC#‚’É"±ëS±!ïG /í‡Àù…`Ýí #0„‚$",‰…Ý¥ê຀ûìn‘ꯤûëÌy“FêÁCoW Ç =£©«´ö§d¸+ñó›˜hà117#, ä{|EÇŠ;¾ÁÓÖ}¦cÝ8v‡jqñp‚ñ¢W—ÎÒJTÇ;5m[|âZí-´Úüi^EŠ rPöÀP|FwÆ‘rÓöW»„+GAÀã —g%=]y¶œ”DÅG3 Ÿ&85ìÅG¤â0‚ÇÄF JPDHu©}ø )"*B0‰Û` B‘ˆØ‡cPηx[c=›œuj:„F7Wû\ô!:RЬLcJB@s…Æ-kçÇ÷ÙîfVXâõÃøÆ‹Ò$¤lèÊÛLíR‰È}»uð E(ª@s‘ #,XAâøÓyí¥Àžn˜½Ã—ê7l;]P®’81žÆßã}ôÐk3>çVgS9ÑÝvôcP¾î)¦‡þ£ò3!NÄ#f^i¬–tZK…õ⇠}K­o™‘+Tê."—mßô¿†ÞЖŸî÷^ýç(V¸³ºþ¾xÁë›Ó±öݺ\çF­¨ªÊÉÀ¸P¯œRtÐ:ÐbÄ„C¡h“Q¤D·[–î¹V&ƒ“$!ô[ý_=µµ#/,ÑîÌ0,Èü1OtOéËÆþÏ</EQU O²QfÅS‹”’9‘/·.±)ŽäöoÖ;¦õŒªãML:p@/SÔúáéDÅÂkÎ}]–IƒB?d3_ÃÞ¼#0Hœú•I¿ÎŽ’1=ØSÊ#/Š0ëDí=x/lBs­•'l@ÙÓÙRŒ?fµëÅÍØ}Y"‘DDkˆ‘ï>­ñoÞïa[F8î®V»ŸÉ»U%Æ*Ùl¦–Í›&Ö g^íl†ç•hÉ0܇Bä±4ôíõýÇ ÝPˆ(>”ƒ*”ŠAd#/Š¿{øßn÷ØýïÍyæB›&dfÉS¦Š KD¢ZJ4“J1”Ù6&…!&Í“e HÆH'Íwìžj€Ÿ¹Åè‘óôEõ&ÛÉ;·^gݤ“fݳ;ãl4&K2É„m1¨ŒácX¹¼sÊ#0€F˜-°ïRàDÜŽcÖ¬Ž¯ xƒ¦m#0P!¤”*­kê²ÐBͲå!<«×YJ²«E9ª`Ä»bÈy²sTñ(BŒ»%ÌàL #/[må›™ép¿<6Úé°¬0»v]ìÞz”:20;÷C±j:ÙÇÇÚ̘–S[B#Ixî<WÈý:´¼Ê‹'Ö81ƒ/Ã÷†èf¸Ë¼5H6 àÖÔxÕm &kükß}jsµ8|ÉPjM7Í Â~ôDH-e°U1’•ºÆÆsnur·¶—“ao ¨­]e™Û¤#5Â2`‰È€ã”#01»²’´ ¦’PpÙX1¬’¢R¶Ì² ¶Œd#/q/[À$ð-+tJ!vŒD*!ò•6’á«ûf4„ÛzÔm¼ëµ¦UʸréF' £IQÃ_^hɵݙQÄO&ä…d¢@¶MP#0‘ÐØÄ™Ve®ìY˜Ùcyj~#/DÇ£F rV±• ^Ïi®«€Òg|‘»‰±q4ÖTñ• QÈÌœƒš<êKøµ‹#/êP+½M¥aV#Š) ›Ã¢œSCH°y¢¥ Ñ®#/•ͶÒÁ6‘\#,ÄH³#³5A”¢ê–"Š‹uÊ’‘¤Á¸EX/FÞI £§žÕlFajÁ¦Œl„G¸­˨§4U!†Ón9Q dj&Ô4âiãBÍj¶65‰¸IjÁA´=#/É„#*‰•Ê«N–;‘;1F9X#,#0m„+‹ë#0…\ª'bB²Z1Ž¶†ë)VèÌ%(ÃRèÜQ(1Q°bT‡K.z:7#ÍÂã€Þ=ìs.îö‰««¬ÍbÈ´î4è6ða4îñÖ´³Z3Ý<cfó<Ñ^m8xæân$;r”¬jŽ2´±°°¬’VA–¬j5ÕÁ¦f0Åd‚íÇà×u j0}=¶³Dº ‚ÁµÃ8a™ïXV2!êôCMSLŽ¼V)8ÖÔZž7iŽ@Ùºc;ëV`7p¤8Û˜–0O#04áLƒÅâ£#¸TqV4 ê23ùN XÕ¤ãT`hdCJ‹t.h„E³(5¡²UL)‡Hš:¸C1‰ªJ‚k?¼ OE“] 51hMâ0v«@‹²…Š0:Hk%iEFW® #/,i#/†"g7$†#0aL0"WYzíãk²§vÝ.›\O•äªïá0tÄÝß#Ã2*ùH åáˆlzé寥´¬[ÌÕÖVòë]4°µê©NØ ºCëhÖh\Ž‡6±dZ*mÉ8~H&Ý¥åΞà›4!¡òTk‹#/\nÁnFXÜÝ'Cd÷'œl#/^B¢WÌô0ÚÎPA’ï‹èëÜK#0Ðæ"%r³óäÌ5)t›F¢*¤'¢Q#/£Ó\‡¿Ô“]šäÖ1@‹uË4¨Ž%EÛàÕƒp0<]5¸Œ6ª$ˆ#,h‘±4ΦÑ%¤lPD—$Ò#0AP%‰*("ŒŠ“#,Ò6&(4en ÆÚ#/‚ š>ÔÑ"+øo8tA•R‰iCsZ½ï{ÍtÕjïzºýþÍÐ{xŽí;’Ü·;ë¾Êþ¤ ÛQm±BÔÙ´Í–Dվ˕ª•Ø‚€û;A²Ÿ¨q^‚ÜLž—uÌõæh¨ÕLUBƒvà‘,uà#0ê}É–7½è¨ƒí”ÃJ'•lúdªÈ¸'ß·ßÔ•T›FÚšf³muš TTÜBåög{'²Hžº”•#,äwW´M Í×O¦Î00Ò,PL …Sëw¤)hÛMöÚHDvˆ75¥Rƒ¥ÆÞ.ë·“ùM¼÷¸ÙÎØŠ66Ý]]*Ûã]t¡ÝÑå5©~Ýx+ц%#0¹˜6#Š.ÄXÕ4é‘BTDûä,u8PƒC-"¯cG.çE8 ÚS®axòà}kan…†ÀzLÛPcìï%p—ç¶Íu>WÓ;$"ΊLß•ÌØŠH‘fŒà*U‡Æ:A»„öÖKܧnx_8Äp ïmº¹'&ÀêP2ùÿItfH“›ðˉIÀÂ\µÊ"e :`]ÅÌ’²ö€ñ4L#G/ÅÓµÈì‹ÛkY<nT’ô\¢öoE²ÓlßMyÎU>[:Àþ#à—@ËgWäGæuaÚˆ7Ñú}Ï¡ØÅWÌä“»¼]qo{œ'ôbÉûÉ|õï¶ýZ&„ŽôЮ;ù„a#/&xQà`¡Ä’$„®ïŠt‰‹–o´é½¦ÐïÅì½CŒ¢q‡XÛQNÁX,å:M,TÙÚPõEB¢‰¹ƒÎ·àpüuçgq@êðû‹ êËU8ê_Œ€“°-‘Ó™Þk¡6j€L¨°LU _¸Úý¦ÛÚùÞ/ÃÅÜJüÝÛ\ýLª£A÷¿Ì~íÞñ­Åê²DI†s‹6´Ò8zŒ1ˆWYUh\¹¾çL7é‘؉®3¦“oÌë,#,ä!lšŠ·üÑÀÓ+áMÈA˜ÒPcr%-'«”jõÍ·‹]5¢ÞµnÄ×'UxÚ5Ë\¬hñ¹µ×mb%Y#/cQ­äÛƯɯm;¾[‘£Y6Þ«…Ñ^/ZwoUs[¦Þ=U° Ð嵃DÖ+å=Áu7”Þ‡OPyŒP½‘£P²’!Î#0 Ñ;Õ¯HúŸãÀq"ÚHÁßÙ·uW¿ ð4 àò‹Ð)ì‚RÁËîD?i @=Óùé%‚"; H2#,UwO·òwŸo5Ékvik¸ÛuÝÂìÉ(ª¡;{‹…wÐiuH$`Þƒ`14P Š¤ˆ²"¢B(´­­t›¥kø_m·¦½ÞÔ¶#0LA»kÇ‘PwDˆ#0ô@ƒBEPwÇâ¨pÐE6M ÿ×#,O8køD<ÝTvé(ÂÕ#0ÀÀ"^)ˆ'žŠX˜ÞT©å{ZPuðé‚J^( ¦b÷bÌ~ã¹#,Nï#T¬´0C èį¢¤NŠƒÓ°2böÄÂCâ P ûF©™²I(aŒjY#-M%#0Òch±e))&d´PÍ«Fµ¬m¶5jm6̪j52ÈY"D"‚I~áǬ»ª€`¶³ VÈ‚191‚•È‘>¼)pn{å{å×..W:uzéïk²ä•:ÃLb(U¤@Ž™1ҮěCeldEˆ’ d• (UF LÒa¥®Ûš•’­ÚU¦ªæ­T’«Zê04_PDj "@'Ëóåk ¦´¥¤ƒZbªMIÛˆ±„F1@ÉV$(•‰Œ*ÑYhuÂâ¡i¶¡Ѥ‡8ü´Ÿ6óVtû¼%þ_F}5¥Ï-R¨¥œ@õ#,„„R‡€gy³ìÌÎ0ŽÜv@ñ8C¼0¦N»>#/–>Øé---,·´bR[â3b¦EyóçË­{¥¹RöTÖ­ÁÉ‹?O¿z’Ý$B¶C¨A¢‡#0—ábŒ ¥V(VpµD±êN Ö‡hëîÙ!DóIGãÏ.ºk«Ë.Ê›3(_¸»ÛUÍFµŠÈ""@ ©Ib!Õâà–9©«&ïàŸÔ¬%Ž#=&(ÕðtÅcJPt9ö/ãŒ>×C$ÚT›´¾“}‘pkqŽ6ô‰?+–…ã\¿Mªgùºo _Kœ‡LTæ'ð$ØMÕLƒ/_<k:F“%#ú•ø¢×50tÆoNé¼*~/jKtý·ƒ²z®¤Ž.8ésÇ•ºÖ]¸rSÃÃôv˜'¼aq<Ù9Ü¥<ã~Ër/¢Žø%aäü\µÂ)NJ4/Ï‘üÓtç$snÍ(ÓJlç|]?«^PìPË8i­cw%òxì;ZkÛíŠòÏ=ÐìÍ?5·fyFi="•#(ï½7ëFf£ŸN{Õ°mŠn=ñÖ¬ÁŠ©féKG»ó¶ü0NJtwÕí¬zæUjW0‡É]x%]pÃ?‰…†~/ZYLÄ’8œn]´‰ï§¶@ßm¹‡¸Þo«ž'vÍ#dN8¢!ÈfB'‰ò{i®–Ø{ÕÝAÁxNjg¶ïrÖ¸n„$Ç:“pTý2üP@ƒœm÷w =P>Ï>~ù”šiê=uv‚G³Ã[ëéºFÉÊêRP¥ŠO5¨©PÞ@]‘AÙKî¥7ÅH‘k»ÄzÍŸIbW(âš­É]Ï'mOßbt±G°¸˜P-V¥©ë…ŸñõØZkpí’^½ºL¬ì©^_°ŒÆâ^Ü—Ȧ¦ ¯d…p±p//Š?{•–ê}œ‡ »è4ûí~ÿÐ6U;:´8…Ìu!ñ=T·!ÕY g ˜x[M¶ËÙb«7 ž\è&ÕøP)ŒÃ±m}£‚¥ysð®¼Ï9Æî’ižÉw¿:`p†¸¨Þ¹’˜èIÖk8`Ö@íCƒphe0Ôl»oÎëò泯•FŠ†|?1 ¦Ö×LÎ „tLÌFÛqVûµáêi[¦6=]Ý:†ŒMªJ;'G£tÔ♳ËÂI$Ýp™æ·*«6ó BÄøÞÔXt±ÛFúe‰q<G&ÔÌS¦2ݶÜޘѳŽ%åfÙbªlChâ8Én>Q³[0HVœqÌDmŽÞ8² Æw+VÔúÊμS/‡Nmciã‡1Îßæõ­Ðûb_zë¹Ú̼’O/šãŠ$ÛO#0JíŠÄH‡K¯‡aÅ!Ë&m­'8’öݪðíä+4ñ{éTó^Â¥WœÏxÑ¥š=Æ8ö­Ëv6]¸–Æü‘~¹òÎm{¼¨®mÍ­Ì_OWÇ›ä9ßï—²²¥H{2a'jKðóžßtÞßKåå4?V÷’yÞ/µ%`Ün‘å‹/*6ãŠÏw“§xÞºkn»ž™gÛ3Zxô‚%ÝûQæô‰FÑQmÚ•mœòœX\iK­‹c©Û jùÆÈwäéSÝT;íþcu(òAèš ¾´ßJü^R·òàêê­ì} æëÒcâý;qíöO@íêÒŠ!DH‹ØÏÇ”qÙcÝtàwž!ã;¦ó¾›®L<÷Ëxk‰–Bgky ¶QKÒs—:”2c~…7 ²›¹³‘»<äÕM Ž(yCCzÓà3]·L„#/o2ÉJy¬oÐÆ#,ôs0¤Ó>6¹>/žÔüañÔÔ#0#,m¢‚ì…BøˆK¼0°Ü»@)-]8—„0‰¸Í3ÕØ™À!ú"Pwü×r.:y`ăx^’‹,I“ZcM‹°ËBz×Q G4¡M ÛFyu &v:šöàs(°.N ac†ÑNŒTÎ9$aJ7õúö˜\¶ nØØC' 8á¹:>“öáëÄ-–4"P½0æ6-rìÜSp¶Ã]ØeцÀË-—‚ìIÒ‹»l!†#00q”:‡íT)¼€¥ê §!#,¢\,"B" $G–¦øhŽFMºxW^Þ¼qË;%·3™BԳá #0Î(çÀÃ-ÒIÏ!®£A26`ƒa™Å„#,¹ã,e¥÷mɬb+"guC+¶‰®&OláEuU¯š2 ²(¡PÆȺËÓììØöÒ¼þù9vl:8Cû´Ú}Òpó-ËTãc­ÓBs$b ƒÜŠŸ‰ ¨—k÷Ä! v×[68Ì©ßQ©t¸}Ðíâ}f£OÕ¶êý•ç@­mÌ¢Gh´®ª&PâÜ©oi0%ßnúš0âÙt[Ox> º7VµÛ¦6)ˆ0Ûƒ{özl(#,Ä?CožN‹kJôNS3‘ö9WÏ=$%CÖÌC[ G¸qÝm³ï]#,ïÇ÷–‹„ds-ÅMVòªw®í’¢Þ0’‡×C‚Z–3oQŠ$ŒÕ©`ãˉK`VZŠè§‡À¡™ç~%‰@½wÙiVkQ-29¾ysæâtÍÅh«†R±Ÿ«–”êâ ˜ÉNtÑÑì›íÓb»6ã–Üyß[á1†+ú-ûâPÝ›$Ë®9˜Kª3lÚY5‚ì[Ô6¾%í^ÛéÔp_îÁ½ãÞ?.y_fêY€òÝûÛžRû®×Þ5s iÖ¸¯)t·Œ©j&cÇxÌ™;%·’Þª|Ü17—”ŽûxÁ{ið#Ü'Šfüo˜ÌÝKŽ6ÁdxAô•µj…q“ÄÊŒ4{¾+ÕÁË,JX!zšZ±-4#0n7ÓIè.®q¬a­H¤óD³íÇLºW2‹$¤¼F—px3„੩Ɉ ÄšÁPæG™Œ†‹Ö]ÑçFO~æO79?QÛ2[ø²:¢zö€¶ä¸Âa#07Öœm—≰6'g=JQÕYqp‚Pg>FC"…*ÒWƒÈð(šZOÅUî]j§'<°ä‡¢î§`öëº=¥#0PcžAä1íí[*‘ªº…CC^#0<‹iõq3¤:dYí¥ZMƒsÊWpÔJ(†Y¸ ÐˆX ŽÐ*AN¤µ‘ÚqXŠôè¼1;éÜv Âh2A¨`¾ä]0ĹŽï9î#0†4奴¾¼lŽ–v¨aQ¨#0xxŸÀ§OP!»Y˜|¬Š\Sض‘<,G@Ao««Q׃5¾ÿ¬+#/ó‘r}4Ó1.ªxƒD@¢ÞlY«}=Å”>gÏh 8Œ#/ž°õ¯alHd{RÑ'W|ÊÀÇ…¶úDÛÕ‡¹ÈâN]~'ƶ¸¢‘U"GÎå±gv#Wìæý’½«X­Þ/jÍç$Fzô/AÈæiRÅQ…&F*ÃÁ¥|kÅ×&¹8u]Û;­Þõy½ñ¶õUűn\—u¹µ%å0³D,Þ*… ”E_T–Û­âÔ[sj¶¹¨P¨¦ù¡Œ,ª i !€¨˜(* Šˆ‚¨£!R€ƒÚzûkâqÛ¡_M…(‘Àu<™ÙØÕ®µ{¤èF,g9¨¢‰æ4¡†[%²S%’Új«_®ÖŠ¶ŠÚ®bI6Æ£B‰ê×ö9·m+ \«x>èf¾—XñËe£ ¥¿ÉÞ*Æ1ƒF(©QL¡ÎùáF–†àA¦í¥ \1Ò?ë~™ƒf–Éà£i”¾iUÉ7b2õšq† V`™ã•Õ¶ R–sã˜òh%Š „… Ñ]R¦EF#‡Ë=HïXjµvDF'`žT‡”¦-&n¦ôRW„Q¤Úm*Ô‘K rªÆÄÆ1¢AJÞb†Zjdh˵…KwL¥ÒHU’ôúË`~+Forrn¨)dÀL$Ô&iÁ‰e—vãÕ•ºÃQ7#,£Œe{èái£¯íe›ÜŒÔÑFš!!¨«XôÖµ¬2#/ÉŒÇqäp¹ròq‹ŒÕ²·Ã+4IQ#0mئ•P¸s@ªÓiåÊ:ˆ&ÚƒVpDVÃ$Îe¯Êî«FúÌAhsS–Q—„l†-A’´Ÿ]><®ÔÝ.›¶NrdF=Ù¦f‚0ÓŒº€,€4C¤#L¤Q!͆–U7«™_ Ò²d¥k .õ±ã#Ž.¥p`Î4Ý€ÞéÇŸh­sb‘'`›’Â7N^mâìÍ ,F¡ö¦ÊDä0x/V†5ßPW…ÖrÕº¡¢ig'D2V0mBFᔪ#0”*à ÖâSC¤Á´Á±‰¦Z6õ¸¶òd²ÊíëÎÅ›[ ¹tøÐA˜Á•£cP¨­tx›I«ƒ«„5±£Tj+ÍŽé» ¦2X6R˜`oeÛûÎF‹Ã‚V²÷Ø;Ú#0#/Dtaý»ˆÓ’?/EFúJ÷ÙÒ&¶µ¶ÆÎӬǰ¤‰±·¼°j°dA¥i«ÊД]Ñe…±BÛ@â2ëF3BÒÑàSÁ„í¬­ƒš”Õ*¥Ggm¾%‹Œg ™R‡…eU[HAÌ Eý‡#0<AZ…LÌ}@eb,hQêE®ú-xݔТïÞš q†àÊtÇ2œAø9­ºÐøkoK fR˜dÔ€Æc8ðM´h¸³#0ÕÍÃ, ‘¦x;„ŒçºWk!®§¨eÃ&ˆ#BÌðM•ƒ™•UÓ3‚-:Ö„D`º#0†#q©½(iZ¤q`8 qC¡ÜKz™dÛCìÁ– íÁ‡Èù…!W›Ì˜ŸEßä %î^sO§âàûZ©L"#0D`¥K.è60bmljŒù‡n¿ïˆW¶¥#,Æ"#"2‰ä/¥`B Pâ”4•¤$‚y×?‰Á( '“ëi?1„‡t#0UNê±Ç¼dQ¦«´þ'b16ÛO hçïKæ˜~¥çe~õ:I3ÌëLo-D’{N°¯h§LúT4úûCt´k-Â#/Èééìðç™;ÀÓÃ}`x“ww_~úgf*1+Úrqfâo.1Áù¼¿R=/ï±´”)v©\\¦gÃŒÞçžÜ o Úc“¹0ˆÁ.;·fÍÝϽ_²0’Ç\èÕáÀyšžp8Lhlˆë“EVomÕÁ(œüÙ„?)ßÓÒšœ8<¼:¾ºŠ–Å tò#,ÃR½˜­»6ÒInªe5e$hCbAªÒI¡"èfÿ×1OdGókÙ=½µcdá÷@dÚ¥EM&¶° ²Š&J‚¦’©•³LšÆ²j+V4”m‰6“c6TXÓMšiP²M$¢”¢TE SPj%"XÓE3FÉ°Ò©¡ik 6aV*"1`B(°’ÎcÄùŠ÷9$!ËÖq:¨b/®%¸Ð{z'c9n>é¹]Öæb{w#,¿âèlüFÆ€‡#,šo§ÑÛmy½6)'Â][A<Ã0 ²¡d‘ ª'¸‚[*¢_A ‘ª ²šôíH—u#/aŒÉH)#/“_G×錦?Š}O!&LÀÁ!„Ù·püLC¸®gNÞ´0Í.EDÄ– ùø†ÛÉ$…Ô:#/6^þG¿ÑæÌ}û:áÏÐÄ1UC)C•´vDûóvôžÍÍôM4(ÕÒúY#,˜6ü#,ò9ÅRFŠÈ”F©6ÑGè-ZæÒ-†š­ˆÏºí\b¬#,…ŠJ¤}Id, ËŸ@`'£9¨Rˆ‰(I#0>ß^apk*ÊÓû)pÇ¿‘pl¡2"?|(µ#/3†çæË£UŒ!òqtxÔ:TQÁ–ŠjB9IˆŠo$ cÓÔå +#/4­ 6<ËRXÒ+H4{˜F›¦øy4vtùóµæQŒ¥:T4¸ŒŠb2Ù)¿mɌУtff0‰XâÒ¡cc#0:@¡…B›P•äèuQ®8âôžŠ ˆT~w ‹’‡$ñP6L1RHtÅ®½—¿gâºóÞ6½mv3£ÖDŽ#/\þÝ9VÙû&Äs¯¼ÜŒR©dXzÈh[ЀòhG£Ò¢L“¸ @ç1$PP†æj©g©–A¶(*¶ˆ! `3!ì6%¡~ÀˆÁUY!º!#,>~Ü× ggšb¥*…$òXH*×£±°žwîÂÒd;¬߶Ù8îî03ŒýiQ<ò†Ìñ½a¿ß…ýo׉1t…Nþ»dÜòòV áÜ=¡ñÌ$GPÁH¹åƒA<dˆÔ”BÍĉ†´kQ3óGœ=ê¿cÓ§µ92Dæö¢Ðñørðþ'„ªô\¦%]»MõöùúM$„…XŒ;¸é#8V Ìô‡DüWÞQwáLB¦¥„tyÆÕ¶ŽÁlÆXÀ™¯âdyû¨âQùÕI!ò*RœZ#K3T&¦1¶™2¡± ±I¢#,Ô¨bîˆãÉvW‚¼­9:Z}^“×`Ò'OˆpóÅÕ#/k朓•®J4óêFEªèÀÆm{I " ‹ÞsØXvc»;­Ë²LÏGÆM˨ꉜ>“-¾J\Â;Ò‚„ª9“^ ›ó0l8Ü`ð,ôMBÝåö8¡¤ÍžŽj'icµbkvPjCQ¡kôüóÝØ:U&£“3åËï„À|¶¾Zü†ý¿Ú¿„ÖV[d’/?(ô/mRŽâb_WéÖî0cEQÂÊîCLϵ}Døf?ă@Í“òWJþ.èœhε,…WMÆ,ÇÙS;?%äyCt¬=0x™1ŠÃOië#/éÛUÒÝ€xw¶#/éO¿ñµü±Ühl2nã#/oMõ⯤Áë›_wC##‡%íùMN8Ó¯2FrÂ#/š“Öb“.‘+Ìèå*F‘£7_›¾qxáR#/xyp°Ï‹8)O ÔU[ÛÆ3YÏ]/}²‚`llÃDAÁ¼HÒU£!#/€&vÄfDÁÄ@A"j!º«ä)_å¥-ƒ+YÞ–Ï(fÓXkZdÓC|²Ì— Ë*aÉ9ŠË'8d±†í1]¥kÎå°˜V&„#,ÆÀ°ô†Î &©³¿”\ú±5,lÁAqªPÁ—KP7îÞh`ÎÌ ÒÎÌp,Ë L6M륕ÉäÀÙÁ5ˆÓ„2¢˜D#/#0r;»#,¸F@0)‘‰š¡ˆm±J@ÐÍL¼¤{ņð4fÒJ‚[Èm£®5êâÕQ¤b%â‘ ©´"؆ùÑÐ\Öô²"DR$[#0á·õÉÇc[ Ï bdHE+²#0o¤Æ3šP´Y&÷f€ ^ Á”j£ÏËüÇ­öég&ü2*Ú 8#/1ê ÄÆ™¥åÇZ³Y #;°tÎ*WšÎÙ¿×ÿ,œ­¸€Ç«¯¦&Ò.À&XpIyënÊLq‡É^›÷9)‚X{OPà»N’Š#/Ù­ŠRèzLË3óË* ,s‘š`ažtÄ¢P…(Åx¥ëIÐdék¬X$€ŒbkJ8A³lÔL+¢ª „„yEÉ4ðï*|¦«‹ ñrMMÅñŒä¹—#0„"&Ëi6ÞöuÍiMíÇT›otÃMˆ\ͱ ùE8þÊU(,¼s§ÃH²±¾U× Ýæ4«#0Öì:1i€$3œŒáÕ„Œ½Fa|­pMk¹uœDu¼¤S­9;.½:b#/"™H‡'‡I³wŒ1¥—ÄlÉ“ètHë6³cZYå’í¢ÝŠá1 ë‡ ´«§$l‚ueI¶ÜŠUf&o±‡f\7M2ÃX=uÑu¾!BÖ¦ yk.¥5)X‚.]7õêH¶BE÷wʼnžŽ3ŽÛ‘#0º¼ ufý¢µo¶&˜ZÑx)FÔÔ|FãÃuná…&LŽÞ¤¶f,%×"kΠ«8c÷½æºuÑjhˆCľÌ,ÛSuSà8W‹™Ù4Žé·r°ý£PX²#0ÐÕ§„%=-Úq;I4²!Äs×HC2T¨âÈJkåÍÝ°ºžÕaÉ–g‡©sv¦&ì¥Çœõë#W'ºM&MÅb)”j7f$m“6CŠvÜÇ<š–ùtÀÒ¥ÅÌëg€qÅŒ«¦n—Y“ò#a‡^ƒ)ÔÃ]­0R/Âo ·Ù—1p-#·#0v( zMT‚L±0K”S#/Κm$Žvš¤ˆ°¡#0ŒIðX§#/N~ض4·8 ÈWz5ˆª#/ÒÒEÑ]$rç|uk*Édñ ñt%Í!r·(çŒCkˆƒ]j$$˜<ŽýÿRßl±§4ûŒlÔ>ý.ŠMBì'˜ªª¸Õ¾;Ç“f€—O·  Vûq†!DºZ:ÙÁntQÓ6‘Â$üïøyJ!í²æ\6E.@ZòXšh??®N:VÞvßT€¤„@ 4SW¼ïωXèÚ°ÍÂ3¿u®qÍ5ídeæJ¸½_B2ßM¢0¥N#/ ÂÒ X{<ÛFó~BÀ™aud¤Ö †l”Åšw–“ã¦âBZß‚3®R¡Z6)¡øf_—;£AËQ1 €Û0UØC˜¡*tžOÏii}ƒ%cP’"6ètà.{Äâ“3™%ÞåÛ³·UjÐ^0JNüÊv#W·hÀ!glÖªÉì(#&B†£ B˜Z'"¬®¢hÊs;@s° vh¦›¦‚Ë.ôŠ¶¾½f­å2CÉœo"/9%‘â”ë“à-MJjsÎG Å{˨˜#/ï¹@áƒ:¿'(ÆÐ4¦øÌa3"ádzJcMös qí”3C°ZèM -<"=1vy®*߀FÉ;ѾΩ††Í‹ŠS“qßbf$’ d$lâ\+H“p3Ç©7Õ$m13”p†Üƒ¤H÷0Æ*šxÐQ†’®*”6MdÔî“Û¥²Ä8:òMhœ¿’ÈÕ€æÌ`Ž›³0ìnð–bEÌÆìZ aH¬NQ™q0º\˜X°#/¹ŒšÉ¤ài)ŒM†TÀÉ(³e’™ÄÜ4Q³3,TT –]Ï7Òu*ö´Ï„mÔZòÚëŠ!‹ŽÖ•\u:]¨Ê4,¨6¨§d15°l&d1™¥!™©@—c& ¤0è”;x¨ÈÉÓ$"àèšØŽ©A —.;#,³a€íhªQ#/ŽDÔ30X op,Ž…%1#/ˆH;”Cq Aƒ9ǺuŒ™IG!›¡,J(sˆHT]S%BÂâI„KHXcX" 'Èž^}'ÀÇ­¸9EA¥¢¢"€ÇѣÜ$Þµ÷x/¤laíä@‡Xhˆ°‰ 6#,ˆTT ‚þÿQo-o`ñ:ŽMao5È:¡~nÚfLCïô™ÞLÏœß i[Êý£¹2aÜÃoPä†ýY{iJ.Z6¯†?j\£¿öJ\ÐMöÙ,0aÃmTø6ÓÁ›BÙßõ¢“õ{ýb]xë1Éî°cùH&èÈå.‰=t"È#À†ú ýUté‰RŠ)Mj‘áxN1׆v1>Yó†8ƒH¸©AÜÔLŽòå8r¹2Å"hñ•¶ìG饾¤5¬¼­ó‘4É/ÆÙQh\Ù€:ibyGò'·ê2caô“œæ”]#,´à V)sKë›–†è+ѸÜíMaö=ju³µÞfàªÕ»º´¢ð:‘GÒ¾šÿL}#0€‘QõÑÞ|Tè8aºDƹ¯¦±ìCÛRÂO²)¶Ên1qè=ù⼂#0á!œ4„ªFNp°Z‘ƒPlr'D’töÒ¡ø›Ì`Tí%‘1,”n2DŠ‡!‘(×’jwÓy»¨•":òÁ1±ØBÉ#0A‘á@BˆD#0‹eY¡##,…Ÿ}#$ÀúÑð[k«o–<wq˾Óc˜Me™1L˜"2m °#,ëŠBbŽ“ÍÔuö>¾ß#,žÉyݦÍÈÒ%ª œC@)³êiŒýJ㈲p4þ©Ñ*$JR1RÓ¹™™!˜"¢´Dƒ@å0²çXL.b²)“_&Ešéâ%ò„óó ¨. ‚›Øƒ‡¯;ÞØî9Ój©<.së#/(ìT`éOe²«ÌÌ…ýž5üå„ü<Ç¡œ?P@z/†I¸hƒ?sxèÑ9g\‘ßÝrõ‚üú5®L…Æ&\³ŒË}©©÷2¦dÛ”dÝË(`"p„è¥=±èTKôl¹à¼RBU­Â¸GSÊ­'¯ÉÔ~ŒSFU¶"ÅÆP—ë†fEb©¬DôÐR?y¶¾C^”`amí0>2˜@“ש"'¶Ðê)(Õ–Õ¥ª‰/Ñ•’Ú©5Fµ¾&¶MZ÷ÛvAäKJŠB{aeZ‰ ¥’ Ý"81‰b15{"´'«˜ÑT †3üð#,Ò &f½¹VßFWè5÷}^ß}­bÒÍ&ŒcXÐB"i{÷÷w‡CçS5Vº™Ã €?B0b"‚) lú0Û·Ú_u6#0Ï>A°\Ú0ŒßôõóI{oéé©ï$±z/|0À(FŠ2BW`@2`ÉPÒ!A¨àL–$’ä ØT„QŠŽÔH™q6' À"­"46”2Z„¡‚FDBÌB „³B¨È 9 ÔoÒèãŒÀ$#/#0ŽÍ1C!Wp…¥#/nbY€ägl¯_b‚v˜ö<ÏGšX`BL+îÀø~£è¡Öi5˜†äͪAªÕ½rp­œÃ¼œüƒËÂXy“p8‰$0#b’$`#,Ü׳–ÄKÓRaÄ£ìjYó*,ßC8¨º!"€£­B™%Fi‰fûEZZ Û'—ÐwAu­uÎík BÀ(x ÅÈLHÊòþÏÝ]}œîL`§·€~V¨b°Hr”JÞ0ªÓ!FlÔ°(ΓJªPÄXÆ»ˆ¨ÓÑj H ’4–ÛR÷Y"p?\™Êᶈk¬§c¾:eOQóõƒì ª› "{ ‚A¦š µ*ƒ ·Â6÷ô¹%÷¶>9u'Ôy«îÜ‘‹d-jÖùü¾Þ†•›Z4ÊË5$©¦¢c†PVÖÌ6¡lÖ”U©-ý*ˆKB…H B‰Ît:0äû%8ú2bº+¹7Ýo|ÍÐO;¢Ü|#,·O1EÃÉ`õ;µãƒXl‚H²íͯ‰§£%;²è½Šnê´Nõ=¶ê ¡ä à΄’??~þ0k:[énìƒÑ…³ü'0ôDѪ‚#0âo}7Ö=u¬¢õ'^„Ö´0㈡4Êv&vái&›ºNî(2@Ž2Â[¼–6PjâÊk­@æ“dÂI¦´[·ÂF#/ÂL²Ùn±…5°|ŽÜq³!´˜Ø2ãBÒgR؃`˜±ÚóOFW%$™C¬éjQ V²ûT#/¹"’#, ²ÒÄ—P.@æ†fe–ͱÛå7À¾r@ˆÇ«º½8›"ˆáYƨèÙ‹ŠšÄ@9{7µºÔ™07ê!ÃäÿŤNîÛ!ÔoÒ$‘ ®¿;Ì %„^â»Öwt¡JU®ßŠ‚\Aô, „'N]ÆþÂ>\ý#/ØÀi/JÕÕž‚b˜ÀzA˜ `D`C® #, Tw=qêå‚p÷ž6á«ÞQ#,+˼ëð< alTðÈÕC‘ãQœVh¦Q+âuóîïÁ"Aº(]¦Xk¾çÏmþ~–+vQ—f3î§Lé#0/ÜÖQHV„Ö½îi÷”j»Ýñíõp¬Â„w"Fî>¡Š/hÙÈ*Ú*ÍÛo÷‘jzIQš~Õöàû÷ƒ'ÎN CÞȃm ×áQ‹ ½Ç÷PÑÒîcšùûŠ¨øq¡O¢&Ò ŽC²(£½_ïˆôØ.Øbuñ؈3^_zŠ0„ÉŒÉç]EEd—vº)"ÃL¯¿ÞdE~ôç¾^y{ÜVpÕ(¦ÈZ1F%y7Y¼“¨¬!ŒF库jfÚÝ„_Ì¥##0`!úR)¨EPÚÇ-Û~Ÿqáã( £×¹O ñ0>g¨< +M ¦#ç6|¯¤#0z•:zï¬$Y‘£(ÂAÄ í:Ì1H(È #b¦V‹%–d¶lÚ¿'çŠú¯ãï¼ih†6Tm%FÒ”ÒÚ¿«ü]ÞhL¾kØŸ2*…DLþÈJZÛì™t!u<ÄFÉi ΄IØ?;“Q0(¤eQNȦÆ*&Ö[E¢ŠŒÛ3Í{çž[RjÒÓhNº-»Ä(?š…úCgý/G§¸ž÷Ïôÿ ƒ³I⯈A6l#/Ãè<¾zÔ×êÈ©¥ÈYìÖá 1>A‹{by"À‡`i€p@Êr2Êaˆ’éÅ>—5EWªÿn1ab)$Iͤú#0¡õQàgHb&7 (ÛhÁpýP7'-=6#0ŠîˆZ³#/0ˆÀüÙ(ú¬‰ˆ¡Í=€TE_b P#0Ÿ¾É Þmh(‘ !¥FˆA#,Š‘¥&©m:œêúnÞSÛ^ßkm»‰€#,‡ôIv(Vè68+MGØ‹#,øÔ*OWO?AËƨg–5CH~íµï°áý¦Ã+ÓšXm¦= άâZR6Ÿš„á#”~¤<ÚCi´Pçk+\[iJ8çÌP?vtÆïs¿}ë6Øeú0ÝEŒLûÌÂécÜ>ñô~àä*×Wëm}z~^7dѪY¦&ÉF£5(¤ ¨¶2j¡M£[ñ­x‰P£j(ÚJeaRÛñþCj T U#0#ï!‚ŒB}Ó&ì$b!T²€„a¤J=ÔT¡Û¯™ÂÄ°¥Û°ÎøOª;×n²Ðw]ÌR#,¿Ú"«WšÑmF+AjPª4&ÔÙb*Å­M-e_±×éÛ㟧¡¿ÎÎíí¥VY) iÏ×Ò©ô¡L ¦¿¦q¦¾Œf µc@#J̤Íø““$ßæ Ã'À}<†ì-¤¤»¢ÎB-­YÚ#J’F‡F†3´ŒgöàÀl(¢I¼´²P(e´O’nÓ`Úh¥M{›#/r–Û]Ý•ã^o+]Sh²kz«’¢Þnë3vfUuÍÛ[PÍdYjòîÆh›]Ý]Ý­&Ê’¦D¦Æ·Õ¼Ó^uuy×vÊœ« R¢#/F*6Û(Á#/2,E´¦–É©2ʹZêÞ]]yçko‹e™V[)kÉ·Wvë,f¬ºk¤ÜÔêɶ Žáª-Ré H…„Ù‚ýQÏl“PæI8hr&Õ‰A?Ã4¤&#0_:3Ú T¶®ýO›Í}4g3 tR¡ùRa1T,½ø˜1©›Àá€!w¼*Ò£úµ601yçd¹ö¢ëþ£Ž_·#,× .ÂIÐG·ÃbzpOK7¢°ŠBºov1&òy›i8°-¶p §fe°æôêI¶áSŽ[¾'ĵ‹Ô#0¦$l²Æ¦÷Ò·N4À¦¦ssÆú¼ŒÌ=rêÏ«o8‚EW(¤g"ð¸7×°˜Þ9¢¿Uì!$Ôè"F\ef^ͱ|3¡w~E‡»)%þ":3 Mäÿ¸ZÖ:>É+HéPyoÇXO©#/!‰åÄT,¶µFPÓ#? Ö±ÛU›Ôç¾Iêd!il…3$Æeðª|g¨Ü5J>ë7í:•#/ñÜpÅ`×L‹áòÒ´ód›¢>Ìëw¤O¬àÁÛ }й(R5œ,#)7˜ÊbÒ¥ ¦Âàs Ym¦fÇÖBl#, ÝE³F]#,¡ îL³>nþÇÈœJº›%ÀE‘/HàC²Æó‹e‰Dh¥§º³J»ê7Ó€îŒaú Xbå 8=‰pâµÑà/ðùI(¿DS‰Bí¹¡ñàËdAÛÝIeÊ<‰£t ý´­I,G#/š½¾FY)a1æ+Òt`fA°)¥KâÅm¹^„æuWÊ­mæÚƒp8QC'øhY‹jr•Ò‚kă#t[Z…];S‡G~ºÜØáÂ\ a9Q¾§S>Ò¨roaÊ!"0ŽCTö /m 6‹' — Hs<Ÿ’¸™œðÍC2Ôå¥Ç5À8eøryŒ0îI¶Á!#ÚqoÈS‰ÒodCŠà{!g#°ñöfb@ÞäMO\5Ê¿g’ÿÑzúw7CðÄ4#,  Z ý9{ñ[Šp+ÊŠCp'V,œþ’ÈD?À¥#° <'’=D|.~ ~~í¶É㆚^ù—rb¡1ýUm#,ŸôôÿñüõÇÿýOöäþŸòÿ‡ÿOýßïüÿíÿßþþoúrÇòþ'ÿ-ÿòÿû·á_üÿþwôüÿŸNŸÿßñÿÃÿßÜ?á÷ýøåÿ~¿ûÿåÿËþ÷Óû¿Õýßòûµÿo¿ýßôÿŸ_Î6ËþŸW×÷ý±ûT틇7Œs#,‚#,¼!œÈ«‡÷ÿÈHˆm%M¤ÁêúØHUÙ2ùz¨¨C™IWÿHþèþ¿¹ÝØ’@“3?·×l¶¿E²%Ë=™ yµÚ]šm×'$,ÆšÈÂlp?¦ÎÿöÞÊa,€cþ{÷íLsÚP\ÒÞ%¹¼Cu¸€ux˜½ÑåZ£#ÜÎ<Šð’ H!Яنcž1cÔ½€V#,5#0ŸêP¦wk|Ï?ËìdÏ3?L¿—jâu+Sb9u(ðÔ˜Ìi¬d›ÅAb¡½ì{ LBH|uû0?àÍUÝÊrI@í{öõ?ùµÙòÎ$|e­1Ìv±•‘ÊÙ™þ‡``Ú•5$Cc#^Í­1±­êh$qƆËH‡® a†Ïù0hÔ!2"ªÔ«ëM"eà;áBdìîµ»“ŒhiyË#0e,¶K Ô=Í<ÐÓt¨£Ì᜺øq›ÔÛTÐâc2,aÅÙ$B ¶º@“0âñ'ì–l,—rF k†Þ±êûiÍ⑳»“Ή%¥é¨C Ê0·—º!žóN¡ì†ƒ/ž™Œ£þšfÂlüë 6P®*f,‚Z"fVHÄb×/;bòÙ#,c.Їí|mÄÎTcæQZ§ðHuo ‘Øáä36lȵétª§A…Ëå—CÖÿʬW¯ýÔƒTö$\ž<ö|г^mæªb»’U˜'4m6&>4“Ĉ˜y†}˜K¸RNEÁBxR²àþÞo~çÃnc^ TZJ”RèåÓ‰]nÄ«ÔÇŽc9™¨" ªž(‚Ñ<í׉n6, ¬Ù 2CpÜ©#/\”3‚”±QS¸YbyÒ4_¥„ « ƒ * §‘Öt(wä¸Ë(D2»Ø@õú/k¥ï|“wù`qÒhfê°sÅÞcÑgy´F#0èÙJS³S~#0ÞO"2˜"‚#0ŒjÅĨ”5QjÊS+lZ’Ô¦’E† „‹R”)ArÊNq#,Š'vgyô‰#/˜Ú0?ØÉ1áTÝo0PE-yàPKµ)+CÙÏÞ#,Ÿ@pÍaþ¢P‰ ÞÛ•Y#,ïIÈ;‰€€ÿkD9Ϙ­?¶Y2IÔCÄý::9ÿ®o&Ø-1ÄÄ%G…ªlÑ•t›?ûoòØh¯¬Lx$Ù-áŽE¾°èèN¶¦ÍÞõ%¾DµÖ#,pŽ`kS•šleÌ®û¡íú]ýd`ÝÒ“lUÍ  i¥SG†fùz1$Ø÷„ùQaNÃ,2U1lííÅÁIè¼e¯¦›×äl¶³eÃ,­øÇW’@Qb“Vƒ*0Y?E¯V+s\ÚM£èÛrÔÒ£ƶõF¯…k{˜ÆÕ&$H*’M##,¦I2ÈY­Øš´½áT,K°â‡'"Œ2HhiúéOî/FîS„d‘#,ÜbèDêv;laðTH"d@l‹ã߸Þt׸|1õ“«jFz)hÜÄO?ý×n¸‹˜œ(4’,€HÍãà)_õžÃéî£Í“ÐŽÔs}\EðâQ‚ú˜pó/Ãbƒÿ® °(mv£¸ìÐwGm\ÙkY*Ó&ºkvÛu–¯¾Ñ6jM{}š­~º(¬Z Eíõ{=Ù?Hþ¾j'báÓåÊwøJ=Þ¹Ž<Œmµ ~.«tKD8èèÅ<¢|%ˆ2ŠO¯ïÅÿ#0Ÿóìûàí&f˜HÁ?øxÉB-à6àyiûÔU'ÿŽ¿ù4j† &0žß÷¶ýBÜ?ËUÜ#/™Å}Á-ZÿïêÿwÞÙ¼‹ñxzþ7_Ÿ~dãW>;ÈqÏNˆñ{Çeý#Çàáíá =‡Œ°·ËçðàxÉá‘ÏÀò¯ýûO§ÈëÿÚcòÿ §ì…ãŒB?ü4»â~Yx”Z?¶,ïgÿšÌµ{åÓ…¥Ô+¨›4ÃÒ#¯á ªßàÌOé Ö•.$ñ‚([ù*Qÿ®‹YÀé]¼N*ˆ¦pAöj¸\eœÁ©‡|Ž!ñõÚtí¢tYXþ^rÄÞ¸bòØKððŠ‰kÊÚs sÇÙÏ,ÀŠÃ$ÉßNñõ]aûQDa›áùþ? ¦Ÿw93êwÕ±íÓãÿ5dxóŒâÅ#/¾“©j¬b/¼<xOí§þÑ#0“Ò=Â"¯“å#,Aÿü]ÉáBBuÅÁ
+#<==
diff --git a/linux_dpdk/ws_main.py b/linux_dpdk/ws_main.py
index 0a6ff7c1..1b6d60ef 100755
--- a/linux_dpdk/ws_main.py
+++ b/linux_dpdk/ws_main.py
@@ -12,6 +12,13 @@ import copy;
import re
import uuid
import subprocess
+import platform
+from waflib import Logs
+from waflib.Configure import conf
+from waflib import Build
+
+# use hostname as part of cache filename
+Build.CACHE_SUFFIX = '_%s_cache.py' % platform.node()
# these variables are mandatory ('/' are converted automatically)
top = '../'
@@ -31,6 +38,16 @@ USERS_ALLOWED_TO_RELEASE = ['hhaim']
# utility for group source code
###################################
+orig_system = os.system
+
+def verify_system(cmd):
+ ret = orig_system(cmd)
+ if ret:
+ raise Exception('Return code %s on command: system("%s")' % (ret, cmd))
+
+os.system = verify_system
+
+
class SrcGroup:
' group of source by directory '
@@ -88,9 +105,81 @@ def options(opt):
opt.add_option('--pkg-file', '--pkg_file', dest='pkg_file', default=False, action='store', help="Destination filename for 'pkg' option.")
opt.add_option('--publish-commit', '--publish_commit', dest='publish_commit', default=False, action='store', help="Specify commit id for 'publish_both' option (Please make sure it's good!)")
+
+def check_ibverbs_deps(bld):
+ if 'LDD' not in bld.env or not len(bld.env['LDD']):
+ bld.fatal('Please run configure. Missing key LDD.')
+ cmd = '%s %s/external_libs/ibverbs/libibverbs.so' % (bld.env['LDD'][0], top)
+ ret, out = getstatusoutput(cmd)
+ if ret or not out:
+ bld.fatal("Command of checking libraries '%s' failed.\nReturn status: %s\nOutput: %s" % (cmd, ret, out))
+ if '=> not found' in out:
+ Logs.pprint('YELLOW', 'Could not find dependency libraries of libibverbs.so:')
+ for line in out.splitlines():
+ if '=> not found' in line:
+ Logs.pprint('YELLOW', line)
+ dumy_libs_path = os.path.abspath(top + 'scripts/dumy_libs')
+ Logs.pprint('YELLOW', 'Adding rpath of %s' % dumy_libs_path)
+ rpath_linkage.append(dumy_libs_path)
+
+
+def missing_pkg_msg(fedora, ubuntu):
+ msg = 'not found\n'
+ fedora_install = 'Fedora install:\nsudo yum install %s\n' % fedora
+ ubuntu_install = 'Ubuntu install:\nsudo apt install %s\n' % ubuntu
+ try:
+ if platform.linux_distribution()[0].capitalize() == 'Ubuntu':
+ msg += ubuntu_install
+ elif platform.linux_distribution()[0].capitalize() == 'Fedora':
+ msg += fedora_install
+ else:
+ raise
+ except:
+ msg += 'Could not determine Linux distribution.\n%s\n%s' % (ubuntu_install, fedora_install)
+ return msg
+
+
+@conf
+def check_ofed(ctx):
+ ctx.start_msg('Checking for OFED')
+ ofed_info='/usr/bin/ofed_info'
+ ofed_ver= '-3.4-'
+ ofed_ver_show= 'v3.4'
+
+ if not os.path.isfile(ofed_info):
+ ctx.end_msg('not found', 'YELLOW')
+ return False
+
+ ret, out = getstatusoutput(ofed_info)
+ if ret:
+ ctx.end_msg("Can't run %s to verify version:\n%s" % (ofed_info, out), 'YELLOW')
+ return False
+
+ lines = out.splitlines()
+ if len(lines) < 2:
+ ctx.end_msg('Expected several output lines from %s, got:\n%s' % (ofed_info, out), 'YELLOW')
+ return False
+
+ if ofed_ver not in lines[0]:
+ ctx.end_msg('Expected version: %s, got: %s.' % (ofed_ver, lines[0]), 'YELLOW')
+ return False
+
+ ctx.end_msg('Found needed version %s' % ofed_ver_show)
+ return True
+
+
def configure(conf):
conf.load('g++')
conf.load('gcc')
+ conf.find_program('ldd')
+ conf.check_cxx(lib = 'z', errmsg = missing_pkg_msg(fedora = 'zlib-devel', ubuntu = 'zlib1g-dev'))
+ ofed_ok = conf.check_ofed(mandatory = False)
+ if ofed_ok:
+ conf.check_cxx(lib = 'ibverbs', errmsg = 'Could not find library ibverbs, will use internal version.', mandatory = False)
+ else:
+ Logs.pprint('YELLOW', 'Warning: will use internal version of ibverbs. If you need to use Mellanox NICs, install OFED:\n' +
+ 'https://trex-tgn.cisco.com/trex/doc/trex_manual.html#_mellanox_connectx_4_support')
+
def getstatusoutput(cmd):
""" Return (status, output) of executing cmd in a shell. Taken from Python3 subprocess.getstatusoutput"""
@@ -127,11 +216,13 @@ main_src = SrcGroup(dir='src',
'time_histogram.cpp',
'os_time.cpp',
'utl_cpuu.cpp',
+ 'utl_ip.cpp',
'utl_json.cpp',
'utl_yaml.cpp',
'nat_check.cpp',
'nat_check_flow_table.cpp',
'msg_manager.cpp',
+ 'trex_port_attr.cpp',
'publisher/trex_publisher.cpp',
'pal/linux_dpdk/pal_utl.cpp',
'pal/linux_dpdk/mbuf.cpp',
@@ -202,7 +293,8 @@ stateless_src = SrcGroup(dir='src/stateless/',
'cp/trex_dp_port_events.cpp',
'dp/trex_stateless_dp_core.cpp',
'messaging/trex_stateless_messaging.cpp',
- 'rx/trex_stateless_rx_core.cpp'
+ 'rx/trex_stateless_rx_core.cpp',
+ 'rx/trex_stateless_rx_port_mngr.cpp'
])
# JSON package
json_src = SrcGroup(dir='external_libs/json',
@@ -315,6 +407,21 @@ dpdk_src = SrcGroup(dir='src/dpdk/',
'drivers/net/ixgbe/ixgbe_pf.c',
'drivers/net/ixgbe/ixgbe_rxtx.c',
'drivers/net/ixgbe/ixgbe_rxtx_vec_sse.c',
+
+ 'drivers/net/mlx5/mlx5_mr.c',
+ 'drivers/net/mlx5/mlx5_ethdev.c',
+ 'drivers/net/mlx5/mlx5_mac.c',
+ 'drivers/net/mlx5/mlx5_rxmode.c',
+ 'drivers/net/mlx5/mlx5_rxtx.c',
+ 'drivers/net/mlx5/mlx5_stats.c',
+ 'drivers/net/mlx5/mlx5_txq.c',
+ 'drivers/net/mlx5/mlx5.c',
+ 'drivers/net/mlx5/mlx5_fdir.c',
+ 'drivers/net/mlx5/mlx5_rss.c',
+ 'drivers/net/mlx5/mlx5_rxq.c',
+ 'drivers/net/mlx5/mlx5_trigger.c',
+ 'drivers/net/mlx5/mlx5_vlan.c',
+
'drivers/net/i40e/base/i40e_adminq.c',
'drivers/net/i40e/base/i40e_common.c',
'drivers/net/i40e/base/i40e_dcb.c',
@@ -515,6 +622,9 @@ includes_path =''' ../src/pal/linux_dpdk/
../src/dpdk/lib/librte_ring/
''';
+
+dpdk_includes_verb_path =''
+
dpdk_includes_path =''' ../src/
../src/pal/linux_dpdk/
../src/pal/linux_dpdk/dpdk
@@ -577,6 +687,8 @@ dpdk_includes_path =''' ../src/
''';
+
+
DPDK_FLAGS=['-D_GNU_SOURCE', '-DPF_DRIVER', '-DX722_SUPPORT', '-DX722_A0_SUPPORT', '-DVF_DRIVER', '-DINTEGRATED_VF'];
client_external_libs = [
@@ -588,6 +700,7 @@ client_external_libs = [
'texttable-0.8.4',
]
+rpath_linkage = []
RELEASE_ = "release"
DEBUG_ = "debug"
@@ -693,6 +806,8 @@ class build_option:
def get_c_flags (self):
flags = self.get_common_flags()
+ if self.isRelease () :
+ flags += ['-DNDEBUG'];
# for C no special flags yet
return (flags)
@@ -719,9 +834,6 @@ build_types = [
def build_prog (bld, build_obj):
- zmq_lib_path='external_libs/zmq/'
- bld.read_shlib( name='zmq' , paths=[top+zmq_lib_path] )
-
#rte_libs =[
# 'dpdk'];
@@ -733,13 +845,11 @@ def build_prog (bld, build_obj):
# add electric fence only for debug image
debug_file_list='';
if not build_obj.isRelease ():
- #debug
debug_file_list +=ef_src.file_list(top)
-
bld.objects(
features='c ',
- includes = dpdk_includes_path,
+ includes = dpdk_includes_path+dpdk_includes_verb_path,
cflags = (build_obj.get_c_flags()+DPDK_FLAGS ),
source = bp_dpdk.file_list(top),
@@ -751,8 +861,9 @@ def build_prog (bld, build_obj):
cxxflags =(build_obj.get_cxx_flags()+['-std=gnu++11',]),
linkflags = build_obj.get_link_flags() ,
lib=['pthread','dl', 'z'],
- use =[build_obj.get_dpdk_target(),'zmq'],
+ use =[build_obj.get_dpdk_target(),'zmq','ibverbs'],
source = bp.file_list(top) + debug_file_list,
+ rpath = rpath_linkage,
target = build_obj.get_target())
@@ -768,8 +879,22 @@ def post_build(bld):
install_single_system(bld, exec_p, obj);
def build(bld):
+ global dpdk_includes_verb_path;
bld.add_pre_fun(pre_build)
bld.add_post_fun(post_build);
+
+ zmq_lib_path='external_libs/zmq/'
+ bld.read_shlib( name='zmq' , paths=[top+zmq_lib_path] )
+ if bld.env['LIB_IBVERBS']:
+ Logs.pprint('GREEN', 'Info: Using external libverbs.')
+ bld.read_shlib(name='ibverbs')
+ else:
+ Logs.pprint('GREEN', 'Info: Using internal libverbs.')
+ ibverbs_lib_path='external_libs/ibverbs/'
+ dpdk_includes_verb_path =' \n ../external_libs/ibverbs/include/ \n'
+ bld.read_shlib( name='ibverbs' , paths=[top+ibverbs_lib_path] )
+ check_ibverbs_deps(bld)
+
for obj in build_types:
build_type(bld,obj);
@@ -883,12 +1008,11 @@ files_list=[
'trex-cfg',
'bp-sim-64',
'bp-sim-64-debug',
- 't-rex-debug-gdb',
+ 't-rex-64-debug-gdb',
'stl-sim',
'find_python.sh',
'run_regression',
'run_functional_tests',
- 'release_notes.pdf',
'dpdk_nic_bind.py',
'dpdk_setup_ports.py',
'doc_process.py',
@@ -898,7 +1022,7 @@ files_list=[
'daemon_server'
];
-files_dir=['cap2','avl','cfg','ko','automation', 'external_libs', 'python-lib','stl','api','exp']
+files_dir=['cap2','avl','cfg','ko','automation', 'external_libs', 'python-lib','stl','exp','dumy_libs']
class Env(object):
diff --git a/scripts/automation/regression/CPlatform.py b/scripts/automation/regression/CPlatform.py
index 0017e7db..606235a6 100755
--- a/scripts/automation/regression/CPlatform.py
+++ b/scripts/automation/regression/CPlatform.py
@@ -499,6 +499,8 @@ class CPlatform(object):
client_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.server_if.get_ipv6_addr(), {'7':1}, ip_type = 'ipv6' )
server_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.client_if.get_ipv6_addr(), {'7':1}, ip_type = 'ipv6' )
+ client_net_next_hop_v4 = misc_methods.get_single_net_client_addr(dual_if.server_if.get_ipv4_addr() )
+ server_net_next_hop_v4 = misc_methods.get_single_net_client_addr(dual_if.client_if.get_ipv4_addr() )
client_if_command_set.append ('{mode}ipv6 enable'.format(mode = unconfig_str))
@@ -526,12 +528,23 @@ class CPlatform(object):
next_hop = server_net_next_hop,
intf = dual_if.client_if.get_name(),
dest_mac = dual_if.client_if.get_ipv6_dest_mac()))
+ # For latency packets (which are IPv4), we need to configure also static ARP
+ conf_t_command_set.append('{mode}arp {next_hop} {dest_mac} arpa'.format(
+ mode = unconfig_str,
+ next_hop = server_net_next_hop_v4,
+ dest_mac = dual_if.client_if.get_ipv6_dest_mac()))
+
if dual_if.server_if.get_ipv6_dest_mac():
conf_t_command_set.append('{mode}ipv6 neighbor {next_hop} {intf} {dest_mac}'.format(
mode = unconfig_str,
- next_hop = client_net_next_hop,
+ next_hop = client_net_next_hop,
intf = dual_if.server_if.get_name(),
dest_mac = dual_if.server_if.get_ipv6_dest_mac()))
+ # For latency packets (which are IPv4), we need to configure also static ARP
+ conf_t_command_set.append('{mode}arp {next_hop} {dest_mac} arpa'.format(
+ mode = unconfig_str,
+ next_hop = client_net_next_hop_v4,
+ dest_mac = dual_if.server_if.get_ipv6_dest_mac()))
conf_t_command_set.append('{mode}route-map {pre}_{p1}_to_{p2} permit 10'.format(
mode = unconfig_str,
diff --git a/scripts/automation/regression/aggregate_results.py b/scripts/automation/regression/aggregate_results.py
index c7c61ea6..130e0545 100755
--- a/scripts/automation/regression/aggregate_results.py
+++ b/scripts/automation/regression/aggregate_results.py
@@ -8,10 +8,8 @@ import sys, os
from collections import OrderedDict
import copy
import datetime, time
-try:
- import cPickle as pickle
-except:
- import pickle
+import traceback
+import yaml
import subprocess, shlex
from ansi2html import Ansi2HTMLConverter
@@ -25,6 +23,15 @@ FUNCTIONAL_CATEGORY = 'Functional' # how to display those categories
ERROR_CATEGORY = 'Error'
+def try_write(file, text):
+ try:
+ file.write(text)
+ except:
+ try:
+ file.write(text.encode('utf-8'))
+ except:
+ file.write(text.decode('utf-8'))
+
def pad_tag(text, tag):
return '<%s>%s</%s>' % (tag, text, tag)
@@ -256,6 +263,7 @@ if __name__ == '__main__':
build_url = os.environ.get('BUILD_URL')
build_id = os.environ.get('BUILD_ID')
trex_repo = os.environ.get('TREX_CORE_REPO')
+ last_commit_info_file = os.environ.get('LAST_COMMIT_INFO')
python_ver = os.environ.get('PYTHON_VER')
if not scenario:
print('Warning: no environment variable SCENARIO, using default')
@@ -283,19 +291,25 @@ if __name__ == '__main__':
trex_last_commit_info = ''
trex_last_commit_hash = trex_info_dict.get('Git SHA')
- if trex_last_commit_hash and trex_repo:
+ if last_commit_info_file and os.path.exists(last_commit_info_file):
+ with open(last_commit_info_file) as f:
+ trex_last_commit_info = f.read().strip().replace('\n', '<br>\n')
+ elif trex_last_commit_hash and trex_repo:
try:
- print('Getting TRex commit with hash %s' % trex_last_commit_hash)
- command = 'git --git-dir %s show %s --quiet' % (trex_repo, trex_last_commit_hash)
+ command = 'git show %s -s' % trex_last_commit_hash
print('Executing: %s' % command)
- proc = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (trex_last_commit_info, stderr) = proc.communicate()
- print('Stdout:\n\t' + trex_last_commit_info.replace('\n', '\n\t'))
- print('Stderr:', stderr)
- print('Return code:', proc.returncode)
- trex_last_commit_info = trex_last_commit_info.replace('\n', '<br>')
+ proc = subprocess.Popen(shlex.split(command), stdout = subprocess.PIPE, stderr = subprocess.STDOUT, cwd = trex_repo)
+ (stdout, stderr) = proc.communicate()
+ stdout = stdout.decode('utf-8', errors = 'replace')
+ print('Stdout:\n\t' + stdout.replace('\n', '\n\t'))
+ if stderr or proc.returncode:
+ print('Return code: %s' % proc.returncode)
+ trex_last_commit_info = stdout.replace('\n', '<br>\n')
except Exception as e:
+ traceback.print_exc()
print('Error getting last commit: %s' % e)
+ else:
+ print('Could not find info about commit!')
##### get xmls: report_<setup name>.xml
@@ -520,7 +534,7 @@ if __name__ == '__main__':
# save html
with open(args.output_htmlfile, 'w') as f:
print('Writing output file: %s' % args.output_htmlfile)
- f.write(html_output)
+ try_write(f, html_output)
html_output = None
# mail report (only error tests, expanded)
@@ -596,7 +610,7 @@ if __name__ == '__main__':
else:
mail_output += add_category_of_tests(ERROR_CATEGORY, error_tests, expanded=True)
else:
- mail_output += '<table><tr style="font-size:120;color:green;font-family:arial"><td>☺</td><td style="font-size:20">All passed.</td></tr></table>\n'
+ mail_output += u'<table><tr style="font-size:120;color:green;font-family:arial"><td>☺</td><td style="font-size:20">All passed.</td></tr></table>\n'
mail_output += '\n</body>\n</html>'
##### save outputs
@@ -605,17 +619,17 @@ if __name__ == '__main__':
# mail content
with open(args.output_mailfile, 'w') as f:
print('Writing output file: %s' % args.output_mailfile)
- f.write(mail_output)
+ try_write(f, mail_output)
# build status
category_dict_status = {}
if os.path.exists(args.build_status_file):
print('Reading: %s' % args.build_status_file)
- with open(args.build_status_file, 'rb') as f:
+ with open(args.build_status_file, 'r') as f:
try:
- category_dict_status = pickle.load(f)
+ category_dict_status = yaml.safe_load(f.read())
except Exception as e:
- print('Error during pickle load: %s' % e)
+ print('Error during YAML load: %s' % e)
if type(category_dict_status) is not dict:
print('%s is corrupt, truncating' % args.build_status_file)
category_dict_status = {}
@@ -635,15 +649,15 @@ if __name__ == '__main__':
current_status = 'Fixed'
category_dict_status[scenario] = current_status
- with open(args.build_status_file, 'wb') as f:
+ with open(args.build_status_file, 'w') as f:
print('Writing output file: %s' % args.build_status_file)
- pickle.dump(category_dict_status, f)
+ yaml.dump(category_dict_status, f)
# last successful commit
- if (current_status in ('Successful', 'Fixed')) and trex_last_commit_hash and jobs_list > 0 and scenario == 'nightly':
+ if (current_status in ('Successful', 'Fixed')) and trex_last_commit_hash and len(jobs_list) > 0 and scenario == 'nightly':
with open(args.last_passed_commit, 'w') as f:
print('Writing output file: %s' % args.last_passed_commit)
- f.write(trex_last_commit_hash)
+ try_write(f, trex_last_commit_hash)
# mail title
mailtitle_output = scenario.capitalize()
@@ -653,7 +667,8 @@ if __name__ == '__main__':
with open(args.output_titlefile, 'w') as f:
print('Writing output file: %s' % args.output_titlefile)
- f.write(mailtitle_output)
+ try_write(f, mailtitle_output)
# exit
+ print('Status: %s' % current_status)
sys.exit(exit_status)
diff --git a/scripts/automation/regression/cfg/client_cfg.yaml b/scripts/automation/regression/cfg/client_cfg.yaml
new file mode 100644
index 00000000..5c3d19ef
--- /dev/null
+++ b/scripts/automation/regression/cfg/client_cfg.yaml
@@ -0,0 +1,48 @@
+#vlan: true
+vlan: false
+
+groups:
+
+- ip_start : 16.0.0.1
+ ip_end : 16.0.1.255
+ initiator :
+ next_hop: 1.1.1.1
+ src_ip : 1.1.1.2
+ responder :
+ next_hop: 1.1.2.1
+ src_ip : 1.1.2.2
+
+ count : 1
+
+- ip_start : 17.0.0.1
+ ip_end : 17.0.1.255
+ initiator :
+ next_hop: 1.1.3.1
+ src_ip : 1.1.3.2
+ responder :
+ next_hop: 1.1.4.1
+ src_ip : 1.1.4.2
+
+ count : 1
+
+- ip_start : 18.0.0.1
+ ip_end : 18.0.1.255
+ initiator :
+ next_hop: 1.1.5.1
+ src_ip : 1.1.5.2
+ responder :
+ next_hop: 1.1.6.1
+ src_ip : 1.1.6.2
+
+ count : 1
+
+- ip_start : 19.0.0.1
+ ip_end : 19.0.1.255
+ initiator :
+ next_hop: 1.1.7.1
+ src_ip : 1.1.7.2
+ responder :
+ next_hop: 1.1.8.1
+ src_ip : 1.1.8.2
+
+ count : 1
diff --git a/scripts/automation/regression/functional_tests/trex_cfg_creator_test.py b/scripts/automation/regression/functional_tests/trex_cfg_creator_test.py
index 5ff6b318..66cb666c 100755
--- a/scripts/automation/regression/functional_tests/trex_cfg_creator_test.py
+++ b/scripts/automation/regression/functional_tests/trex_cfg_creator_test.py
@@ -25,7 +25,7 @@ def compare_lines(golden, output):
raise CompareLinesNumDiff('Number of lines on golden is: %s, in output: %s\nGolden:\n%s\nGenerated:\n%s\n' % (len(golden_lines), len(output_lines), golden, output))
for line_num, (golden_line, output_line) in enumerate(zip(golden_lines, output_lines)):
if golden_line != output_line:
- raise CompareLinesDiff('Produced YAML differs from golden at line %s.Golden: %s <-> Output: %s' % (line_num + 1, golden_line, output_line))
+ raise CompareLinesDiff('Produced YAML differs from golden at line %s.\nGolden: %s <-> Output: %s' % (line_num + 1, golden_line, output_line))
def create_config(cpu_topology, interfaces, *args, **kwargs):
config = ConfigCreator(cpu_topology, interfaces, *args, **kwargs)
@@ -112,7 +112,7 @@ class TRexCfgCreator_Test:
latency_thread_id: 1
dual_if:
- socket: 0
- threads: [2]
+ threads: [2,3,4]
'''
output = create_config(cpu_topology, interfaces)
verify_master_core0(output)
@@ -308,16 +308,16 @@ class TRexCfgCreator_Test:
platform:
master_thread_id: 0
- latency_thread_id: 16
+ latency_thread_id: 12
dual_if:
- socket: 0
- threads: [1,17,2,18,3,19,4]
+ threads: [1,2,3,16,17,18,19]
- socket: 1
- threads: [8,24,9,25,10,26,11]
+ threads: [8,9,10,11,24,25,26]
- socket: 0
- threads: [20,5,21,6,22,7,23]
+ threads: [4,5,6,7,20,21,22]
'''
output = create_config(cpu_topology, interfaces)
verify_master_core0(output)
@@ -446,10 +446,10 @@ class TRexCfgCreator_Test:
latency_thread_id: 31
dual_if:
- socket: 0
- threads: [1,17,2,18,3,19,4,20,5,21,6,22,7,23,16]
+ threads: [1,2,3,4,5,6,7,16,17,18,19,20,21,22,23]
- socket: 1
- threads: [8,24,9,25,10,26,11,27,12,28,13,29,14,30,15]
+ threads: [8,9,10,11,12,13,14,15,24,25,26,27,28,29,30]
'''
output = create_config(cpu_topology, interfaces)
verify_master_core0(output)
@@ -575,13 +575,13 @@ class TRexCfgCreator_Test:
platform:
master_thread_id: 0
- latency_thread_id: 16
+ latency_thread_id: 8
dual_if:
- socket: 0
- threads: [1,17,2,18,3,19,4]
+ threads: [1,2,3,16,17,18,19]
- socket: 0
- threads: [20,5,21,6,22,7,23]
+ threads: [4,5,6,7,20,21,22]
'''
output = create_config(cpu_topology, interfaces)
verify_master_core0(output)
@@ -694,5 +694,6 @@ class TRexCfgCreator_Test:
@classmethod
def tearDownClass(cls):
- sys.path.remove(CTRexScenario.scripts_path)
+ if CTRexScenario.scripts_path in sys.path:
+ sys.path.remove(CTRexScenario.scripts_path)
del sys.modules['dpdk_setup_ports']
diff --git a/scripts/automation/regression/setups/trex15/benchmark.yaml b/scripts/automation/regression/setups/trex03/benchmark.yaml
index b366b3fb..b366b3fb 100644
--- a/scripts/automation/regression/setups/trex15/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex03/benchmark.yaml
diff --git a/scripts/automation/regression/setups/trex17/config.yaml b/scripts/automation/regression/setups/trex03/config.yaml
index 7ad6a20a..17c4c91e 100644
--- a/scripts/automation/regression/setups/trex17/config.yaml
+++ b/scripts/automation/regression/setups/trex03/config.yaml
@@ -34,6 +34,6 @@
# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
trex:
- hostname : csi-trex-17
+ hostname : csi-trex-03
cores : 1
modes : [loopback, virt_nics, VM]
diff --git a/scripts/automation/regression/setups/trex17/benchmark.yaml b/scripts/automation/regression/setups/trex06/benchmark.yaml
index 8bc9d29c..2f51a3fd 100644
--- a/scripts/automation/regression/setups/trex17/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex06/benchmark.yaml
@@ -140,7 +140,7 @@ test_CPU_benchmark:
bw_per_core : 1
- name : stl/pcap.py
- kwargs : {ipg_usec: 2, loop_count: 0}
+ kwargs : {ipg_usec: 3, loop_count: 0}
cpu_util : 1
bw_per_core : 1
diff --git a/scripts/automation/regression/setups/trex15/config.yaml b/scripts/automation/regression/setups/trex06/config.yaml
index c5fc3b22..da0eb2dd 100644
--- a/scripts/automation/regression/setups/trex15/config.yaml
+++ b/scripts/automation/regression/setups/trex06/config.yaml
@@ -34,6 +34,6 @@
# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
trex:
- hostname : csi-trex-15
+ hostname : csi-trex-06
cores : 1
modes : [loopback, virt_nics, VM]
diff --git a/scripts/automation/regression/setups/trex07/backup/benchmark.yaml b/scripts/automation/regression/setups/trex07/backup/benchmark.yaml
new file mode 100644
index 00000000..0dc340b0
--- /dev/null
+++ b/scripts/automation/regression/setups/trex07/backup/benchmark.yaml
@@ -0,0 +1,244 @@
+###############################################################
+#### TRex benchmark configuration file ####
+###############################################################
+
+#### common templates ###
+
+stat_route_dict: &stat_route_dict
+ clients_start : 16.0.0.1
+ servers_start : 48.0.0.1
+ dual_port_mask : 1.0.0.0
+ client_destination_mask : 255.0.0.0
+ server_destination_mask : 255.0.0.0
+
+nat_dict: &nat_dict
+ clients_net_start : 16.0.0.0
+ client_acl_wildcard_mask : 0.0.0.255
+ dual_port_mask : 1.0.0.0
+ pool_start : 200.0.0.0
+ pool_netmask : 255.255.255.0
+
+
+### stateful ###
+
+test_jumbo:
+ multiplier : 17
+ cores : 1
+ bw_per_core : 543.232
+
+
+test_routing_imix:
+ multiplier : 10
+ cores : 1
+ bw_per_core : 34.128
+
+
+test_routing_imix_64:
+ multiplier : 430
+ cores : 1
+ bw_per_core : 5.893
+
+
+test_static_routing_imix: &test_static_routing_imix
+ stat_route_dict : *stat_route_dict
+ multiplier : 8
+ cores : 1
+ bw_per_core : 34.339
+
+test_static_routing_imix_asymmetric: *test_static_routing_imix
+
+
+test_ipv6_simple:
+ multiplier : 9
+ cores : 2
+ bw_per_core : 19.064
+
+
+test_nat_simple_mode1: &test_nat_simple
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+ multiplier : 6000
+ cores : 1
+ nat_opened : 500000
+ allow_timeout_dev : True
+ bw_per_core : 44.445
+
+test_nat_simple_mode2: *test_nat_simple
+
+test_nat_simple_mode3: *test_nat_simple
+
+test_nat_learning: *test_nat_simple
+
+
+test_nbar_simple:
+ multiplier : 7.5
+ cores : 2
+ bw_per_core : 17.174
+ nbar_classification:
+ rtp : 32.57
+ http : 30.25
+ oracle_sqlnet : 11.23
+ exchange : 10.80
+ citrix : 5.62
+ rtsp : 2.84
+ dns : 1.95
+ smtp : 0.57
+ pop3 : 0.36
+ ssl : 0.17
+ sctp : 0.13
+ sip : 0.09
+ unknown : 3.41
+
+
+test_rx_check_http: &rx_http
+ multiplier : 15000
+ cores : 1
+ rx_sample_rate : 16
+ bw_per_core : 39.560
+
+test_rx_check_http_ipv6:
+ << : *rx_http
+ bw_per_core : 49.237
+
+test_rx_check_http_negative_disabled:
+ << : *rx_http
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+
+
+test_rx_check_sfr: &rx_sfr
+ multiplier : 10
+ cores : 3
+ rx_sample_rate : 16
+ bw_per_core : 16.082
+
+test_rx_check_sfr_ipv6:
+ << : *rx_sfr
+ bw_per_core : 19.198
+
+
+
+### stateless ###
+
+test_CPU_benchmark:
+ profiles:
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# causes queue full
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 64, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# not enough memory + queue full if memory increase
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 9000, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/imix.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/pcap.py
+ kwargs : {ipg_usec: 2, loop_count: 0}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/hlt/hlt_udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+
diff --git a/scripts/automation/regression/setups/trex07/backup/config.yaml b/scripts/automation/regression/setups/trex07/backup/config.yaml
new file mode 100644
index 00000000..db6e9bf8
--- /dev/null
+++ b/scripts/automation/regression/setups/trex07/backup/config.yaml
@@ -0,0 +1,66 @@
+################################################################
+#### TRex nightly test configuration file ####
+################################################################
+
+
+### TRex configuration:
+# hostname - can be DNS name or IP for the TRex machine for ssh to the box
+# password - root password for TRex machine
+# is_dual - should the TRex inject with -p ?
+# version_path - path to the TRex version and executable
+# cores - how many cores should be used
+# latency - rate of latency packets injected by the TRex
+# modes - list of modes (tagging) of this setup (loopback etc.)
+# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
+# * VM - Virtual OS (accept low CPU utilization in tests, latency can get spikes)
+# * virt_nics - NICs are virtual (VMXNET3 etc.)
+
+### Router configuration:
+# hostname - the router hostname as apears in ______# cli prefix
+# ip_address - the router's ip that can be used to communicate with
+# image - the desired imaged wished to be loaded as the router's running config
+# line_password - router password when access via Telent
+# en_password - router password when changing to "enable" mode
+# interfaces - an array of client-server pairs, representing the interfaces configurations of the router
+# configurations - an array of configurations that could possibly loaded into the router during the test.
+# The "clean" configuration is a mandatory configuration the router will load with to run the basic test bench
+
+### TFTP configuration:
+# hostname - the tftp hostname
+# ip_address - the tftp's ip address
+# images_path - the tftp's relative path in which the router's images are located
+
+### Test_misc configuration:
+# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
+
+trex:
+ hostname : csi-trex-07
+ cores : 4
+
+router:
+ model : ASR1001x
+ hostname : csi-asr-01
+ ip_address : 10.56.216.120
+ image : asr1001x-universalk9.03.13.02.S.154-3.S2-ext.SPA.bin
+ line_password : cisco
+ en_password : cisco
+ mgmt_interface : GigabitEthernet0
+ clean_config : clean_config.cfg
+ intf_masking : 255.255.255.0
+ ipv6_mask : 64
+ interfaces :
+ - client :
+ name : Te0/0/0
+ src_mac_addr : 0000.0001.0002
+ dest_mac_addr : 0000.0001.0001
+ server :
+ name : Te0/0/1
+ src_mac_addr : 0000.0002.0002
+ dest_mac_addr : 0000.0002.0001
+ vrf_name : null
+
+tftp:
+ hostname : ats-asr-srv-1
+ ip_address : 10.56.217.7
+ root_dir : /scratch/tftp/
+ images_path : /asr1001x/
diff --git a/scripts/automation/regression/setups/trex07/benchmark.yaml b/scripts/automation/regression/setups/trex07/benchmark.yaml
index 0dc340b0..6e861836 100644
--- a/scripts/automation/regression/setups/trex07/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex07/benchmark.yaml
@@ -4,120 +4,57 @@
#### common templates ###
-stat_route_dict: &stat_route_dict
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
-
-nat_dict: &nat_dict
- clients_net_start : 16.0.0.0
- client_acl_wildcard_mask : 0.0.0.255
- dual_port_mask : 1.0.0.0
- pool_start : 200.0.0.0
- pool_netmask : 255.255.255.0
-
-
-### stateful ###
-
test_jumbo:
- multiplier : 17
- cores : 1
- bw_per_core : 543.232
+ multiplier : 120
+ cores : 2
+ bw_per_core : 962.464
test_routing_imix:
- multiplier : 10
- cores : 1
- bw_per_core : 34.128
+ multiplier : 60
+ cores : 4
+ bw_per_core : 48.130
test_routing_imix_64:
- multiplier : 430
- cores : 1
- bw_per_core : 5.893
-
+ multiplier : 4000
+ cores : 7
+ bw_per_core : 12.699
-test_static_routing_imix: &test_static_routing_imix
- stat_route_dict : *stat_route_dict
- multiplier : 8
- cores : 1
- bw_per_core : 34.339
-test_static_routing_imix_asymmetric: *test_static_routing_imix
+test_static_routing_imix_asymmetric:
+ multiplier : 50
+ cores : 3
+ bw_per_core : 50.561
test_ipv6_simple:
- multiplier : 9
- cores : 2
- bw_per_core : 19.064
-
-
-test_nat_simple_mode1: &test_nat_simple
- stat_route_dict : *stat_route_dict
- nat_dict : *nat_dict
- multiplier : 6000
- cores : 1
- nat_opened : 500000
- allow_timeout_dev : True
- bw_per_core : 44.445
-
-test_nat_simple_mode2: *test_nat_simple
-
-test_nat_simple_mode3: *test_nat_simple
-
-test_nat_learning: *test_nat_simple
-
-
-test_nbar_simple:
- multiplier : 7.5
- cores : 2
- bw_per_core : 17.174
- nbar_classification:
- rtp : 32.57
- http : 30.25
- oracle_sqlnet : 11.23
- exchange : 10.80
- citrix : 5.62
- rtsp : 2.84
- dns : 1.95
- smtp : 0.57
- pop3 : 0.36
- ssl : 0.17
- sctp : 0.13
- sip : 0.09
- unknown : 3.41
+ multiplier : 50
+ cores : 7
+ bw_per_core : 19.5
test_rx_check_http: &rx_http
- multiplier : 15000
- cores : 1
- rx_sample_rate : 16
- bw_per_core : 39.560
+ multiplier : 99000
+ cores : 7
+ rx_sample_rate : 128
+ bw_per_core : 49.464
test_rx_check_http_ipv6:
<< : *rx_http
bw_per_core : 49.237
-test_rx_check_http_negative_disabled:
- << : *rx_http
- stat_route_dict : *stat_route_dict
- nat_dict : *nat_dict
-
-
test_rx_check_sfr: &rx_sfr
- multiplier : 10
- cores : 3
- rx_sample_rate : 16
- bw_per_core : 16.082
+ multiplier : 35
+ cores : 7
+ rx_sample_rate : 128
+ bw_per_core : 20.871
test_rx_check_sfr_ipv6:
<< : *rx_sfr
bw_per_core : 19.198
-
### stateless ###
test_CPU_benchmark:
@@ -178,10 +115,10 @@ test_CPU_benchmark:
cpu_util : 1
bw_per_core : 1
- - name : stl/udp_for_benchmarks.py
- kwargs : {packet_len: 9000, stream_count: 100}
- cpu_util : 1
- bw_per_core : 1
+ #- name : stl/udp_for_benchmarks.py
+ # kwargs : {packet_len: 9000, stream_count: 100}
+ # cpu_util : 1
+ # bw_per_core : 1
# not enough memory + queue full if memory increase
# - name : stl/udp_for_benchmarks.py
@@ -241,4 +178,56 @@ test_CPU_benchmark:
cpu_util : 1
bw_per_core : 1
-
+test_performance_vm_single_cpu:
+ cfg:
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 9.6
+ max: 13.3
+
+
+test_performance_vm_single_cpu_cached:
+ cfg:
+ mult : "10%"
+ mpps_per_core_golden :
+ min: 16.0
+ max: 25.0
+
+
+
+test_performance_syn_attack_single_cpu:
+ cfg:
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 9.0
+ max: 14.0
+
+test_performance_vm_multi_cpus:
+ cfg:
+ core_count : 7
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 8.5
+ max: 12.0
+
+
+test_performance_vm_multi_cpus_cached:
+ cfg:
+ core_count : 7
+ mult : "35%"
+ mpps_per_core_golden :
+ min: 9.0
+ max: 15.0
+
+test_performance_syn_attack_multi_cpus:
+ cfg:
+ core_count : 7
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 8.0
+ max: 16.0
+
+
+test_all_profiles :
+ mult : "5%"
+
diff --git a/scripts/automation/regression/setups/trex07/config.yaml b/scripts/automation/regression/setups/trex07/config.yaml
index db6e9bf8..10472c4f 100644
--- a/scripts/automation/regression/setups/trex07/config.yaml
+++ b/scripts/automation/regression/setups/trex07/config.yaml
@@ -35,32 +35,7 @@
trex:
hostname : csi-trex-07
- cores : 4
+ cores : 8
+ modes : ['loopback']
-router:
- model : ASR1001x
- hostname : csi-asr-01
- ip_address : 10.56.216.120
- image : asr1001x-universalk9.03.13.02.S.154-3.S2-ext.SPA.bin
- line_password : cisco
- en_password : cisco
- mgmt_interface : GigabitEthernet0
- clean_config : clean_config.cfg
- intf_masking : 255.255.255.0
- ipv6_mask : 64
- interfaces :
- - client :
- name : Te0/0/0
- src_mac_addr : 0000.0001.0002
- dest_mac_addr : 0000.0001.0001
- server :
- name : Te0/0/1
- src_mac_addr : 0000.0002.0002
- dest_mac_addr : 0000.0002.0001
- vrf_name : null
-tftp:
- hostname : ats-asr-srv-1
- ip_address : 10.56.217.7
- root_dir : /scratch/tftp/
- images_path : /asr1001x/
diff --git a/scripts/automation/regression/setups/trex08/benchmark.yaml b/scripts/automation/regression/setups/trex08/benchmark.yaml
index 8f83e8f9..935b3e55 100644
--- a/scripts/automation/regression/setups/trex08/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex08/benchmark.yaml
@@ -179,3 +179,53 @@ test_CPU_benchmark:
bw_per_core : 1
+test_performance_vm_single_cpu:
+ cfg:
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 15.1
+ max: 20.3
+
+
+test_performance_vm_single_cpu_cached:
+ cfg:
+ mult : "10%"
+ mpps_per_core_golden :
+ min: 29.1
+ max: 32.0
+
+
+
+test_performance_syn_attack_single_cpu:
+ cfg:
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 13.2
+ max: 15.0
+
+test_performance_vm_multi_cpus:
+ cfg:
+ core_count : 7
+ mult : "40%"
+ mpps_per_core_golden :
+ min: 15.0
+ max: 20.0
+
+
+test_performance_vm_multi_cpus_cached:
+ cfg:
+ core_count : 7
+ mult : "40%"
+ mpps_per_core_golden :
+ min: 29.0
+ max: 34.0
+
+test_performance_syn_attack_multi_cpus:
+ cfg:
+ core_count : 7
+ mult : "40%"
+ mpps_per_core_golden :
+ min: 13.0
+ max: 17.0
+
+
diff --git a/scripts/automation/regression/setups/trex09/benchmark.yaml b/scripts/automation/regression/setups/trex09/benchmark.yaml
index d1f5f56c..50f08351 100644
--- a/scripts/automation/regression/setups/trex09/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex09/benchmark.yaml
@@ -187,7 +187,7 @@ test_performance_vm_single_cpu:
cfg:
mult : "90%"
mpps_per_core_golden :
- min: 16.2
+ min: 13.0
max: 17.3
@@ -195,7 +195,7 @@ test_performance_vm_single_cpu_cached:
cfg:
mult : "90%"
mpps_per_core_golden :
- min: 29.5
+ min: 28.5
max: 31.2
@@ -221,7 +221,7 @@ test_performance_vm_multi_cpus_cached:
core_count : 2
mult : "90%"
mpps_per_core_golden :
- min: 28.8
+ min: 26.8
max: 29.5
test_performance_syn_attack_multi_cpus:
diff --git a/scripts/automation/regression/setups/trex11/backup/benchmark.yaml b/scripts/automation/regression/setups/trex11/backup/benchmark.yaml
new file mode 100644
index 00000000..b366b3fb
--- /dev/null
+++ b/scripts/automation/regression/setups/trex11/backup/benchmark.yaml
@@ -0,0 +1,155 @@
+################################################################
+#### TRex benchmark configuration file ####
+################################################################
+
+### stateful ###
+
+test_jumbo:
+ multiplier : 2.8
+ cores : 1
+ bw_per_core : 106.652
+
+
+test_routing_imix:
+ multiplier : 0.5
+ cores : 1
+ bw_per_core : 11.577
+
+
+test_routing_imix_64:
+ multiplier : 28
+ cores : 1
+ bw_per_core : 2.030
+
+
+test_static_routing_imix_asymmetric:
+ multiplier : 0.8
+ cores : 1
+ bw_per_core : 13.742
+
+
+
+### stateless ###
+
+test_CPU_benchmark:
+ profiles:
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# causes queue full
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 64, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# not enough memory + queue full if memory increase
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 9000, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/imix.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/pcap.py
+ kwargs : {ipg_usec: 4, loop_count: 0}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/hlt/hlt_udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+
diff --git a/scripts/automation/regression/setups/trex11/backup/config.yaml b/scripts/automation/regression/setups/trex11/backup/config.yaml
new file mode 100644
index 00000000..782b7542
--- /dev/null
+++ b/scripts/automation/regression/setups/trex11/backup/config.yaml
@@ -0,0 +1,38 @@
+################################################################
+#### TRex nightly test configuration file ####
+################################################################
+
+
+### TRex configuration:
+# hostname - can be DNS name or IP for the TRex machine for ssh to the box
+# password - root password for TRex machine
+# is_dual - should the TRex inject with -p ?
+# version_path - path to the TRex version and executable
+# cores - how many cores should be used
+# latency - rate of latency packets injected by the TRex
+# modes - list of modes (tagging) of this setup (loopback, virtual etc.)
+# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
+# * virtual - virtual OS (accept low CPU utilization in tests)
+
+### Router configuration:
+# hostname - the router hostname as apears in ______# cli prefix
+# ip_address - the router's ip that can be used to communicate with
+# image - the desired imaged wished to be loaded as the router's running config
+# line_password - router password when access via Telent
+# en_password - router password when changing to "enable" mode
+# interfaces - an array of client-server pairs, representing the interfaces configurations of the router
+# configurations - an array of configurations that could possibly loaded into the router during the test.
+# The "clean" configuration is a mandatory configuration the router will load with to run the basic test bench
+
+### TFTP configuration:
+# hostname - the tftp hostname
+# ip_address - the tftp's ip address
+# images_path - the tftp's relative path in which the router's images are located
+
+### Test_misc configuration:
+# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
+
+trex:
+ hostname : csi-trex-11
+ cores : 1
+ modes : ['loopback', 'VM', 'virt_nics']
diff --git a/scripts/automation/regression/setups/trex11/benchmark.yaml b/scripts/automation/regression/setups/trex11/benchmark.yaml
index b366b3fb..5ebcdd55 100644
--- a/scripts/automation/regression/setups/trex11/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex11/benchmark.yaml
@@ -1,32 +1,58 @@
-################################################################
+###############################################################
#### TRex benchmark configuration file ####
-################################################################
+###############################################################
-### stateful ###
+#### common templates ###
-test_jumbo:
- multiplier : 2.8
- cores : 1
- bw_per_core : 106.652
+#test_jumbo:
+# multiplier : 2.8
+# cores : 1
+# bw_per_core : 962.464
test_routing_imix:
multiplier : 0.5
cores : 1
- bw_per_core : 11.577
+ bw_per_core : 48.130
test_routing_imix_64:
multiplier : 28
cores : 1
- bw_per_core : 2.030
+ bw_per_core : 12.699
test_static_routing_imix_asymmetric:
- multiplier : 0.8
+ multiplier : 0.5
+ cores : 1
+ bw_per_core : 50.561
+
+
+test_ipv6_simple:
+ multiplier : 0.5
+ cores : 1
+ bw_per_core : 19.5
+
+
+test_rx_check_http: &rx_http
+ multiplier : 1000
+ cores : 1
+ rx_sample_rate : 128
+ bw_per_core : 49.464
+
+test_rx_check_http_ipv6:
+ << : *rx_http
+ bw_per_core : 49.237
+
+test_rx_check_sfr: &rx_sfr
+ multiplier : 8
cores : 1
- bw_per_core : 13.742
+ rx_sample_rate : 128
+ bw_per_core : 20.9
+test_rx_check_sfr_ipv6:
+ << : *rx_sfr
+ bw_per_core : 23.9
### stateless ###
@@ -140,16 +166,21 @@ test_CPU_benchmark:
bw_per_core : 1
- name : stl/pcap.py
- kwargs : {ipg_usec: 4, loop_count: 0}
+ kwargs : {ipg_usec: 2, loop_count: 0}
cpu_util : 1
bw_per_core : 1
- - name : stl/udp_rand_len_9k.py
- cpu_util : 1
- bw_per_core : 1
+ #- name : stl/udp_rand_len_9k.py
+ # cpu_util : 1
+ # bw_per_core : 1
- - name : stl/hlt/hlt_udp_rand_len_9k.py
- cpu_util : 1
- bw_per_core : 1
+ #- name : stl/hlt/hlt_udp_rand_len_9k.py
+ # cpu_util : 1
+ # bw_per_core : 1
+
+test_all_profiles :
+ mult : "5%"
+ skip : ['udp_rand_len_9k.py','udp_inc_len_9k.py'] # due to VIC 9K defect trex-282
+
diff --git a/scripts/automation/regression/setups/trex11/config.yaml b/scripts/automation/regression/setups/trex11/config.yaml
index 782b7542..393c8749 100644
--- a/scripts/automation/regression/setups/trex11/config.yaml
+++ b/scripts/automation/regression/setups/trex11/config.yaml
@@ -4,15 +4,16 @@
### TRex configuration:
-# hostname - can be DNS name or IP for the TRex machine for ssh to the box
-# password - root password for TRex machine
-# is_dual - should the TRex inject with -p ?
-# version_path - path to the TRex version and executable
-# cores - how many cores should be used
-# latency - rate of latency packets injected by the TRex
-# modes - list of modes (tagging) of this setup (loopback, virtual etc.)
-# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
-# * virtual - virtual OS (accept low CPU utilization in tests)
+# hostname - can be DNS name or IP for the TRex machine for ssh to the box
+# password - root password for TRex machine
+# is_dual - should the TRex inject with -p ?
+# version_path - path to the TRex version and executable
+# cores - how many cores should be used
+# latency - rate of latency packets injected by the TRex
+# modes - list of modes (tagging) of this setup (loopback etc.)
+# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
+# * VM - Virtual OS (accept low CPU utilization in tests, latency can get spikes)
+# * virt_nics - NICs are virtual (VMXNET3 etc.)
### Router configuration:
# hostname - the router hostname as apears in ______# cli prefix
@@ -35,4 +36,6 @@
trex:
hostname : csi-trex-11
cores : 1
- modes : ['loopback', 'VM', 'virt_nics']
+ modes : ['loopback']
+
+
diff --git a/scripts/automation/regression/setups/trex14/BU/benchmark.yaml b/scripts/automation/regression/setups/trex14/BU/benchmark.yaml
new file mode 100644
index 00000000..04f13e79
--- /dev/null
+++ b/scripts/automation/regression/setups/trex14/BU/benchmark.yaml
@@ -0,0 +1,245 @@
+###############################################################
+#### TRex benchmark configuration file ####
+###############################################################
+
+#### common templates ###
+
+stat_route_dict: &stat_route_dict
+ clients_start : 16.0.0.1
+ servers_start : 48.0.0.1
+ dual_port_mask : 1.0.0.0
+ client_destination_mask : 255.0.0.0
+ server_destination_mask : 255.0.0.0
+
+nat_dict: &nat_dict
+ clients_net_start : 16.0.0.0
+ client_acl_wildcard_mask : 0.0.0.255
+ dual_port_mask : 1.0.0.0
+ pool_start : 200.0.0.0
+ pool_netmask : 255.255.255.0
+
+
+### stateful ###
+
+test_jumbo:
+ multiplier : 17
+ cores : 1
+ bw_per_core : 543.232
+
+
+test_routing_imix:
+ multiplier : 10
+ cores : 1
+ bw_per_core : 34.128
+
+
+test_routing_imix_64:
+ multiplier : 430
+ cores : 1
+ bw_per_core : 5.893
+
+
+test_static_routing_imix: &test_static_routing_imix
+ stat_route_dict : *stat_route_dict
+ multiplier : 8
+ cores : 1
+ bw_per_core : 34.339
+
+test_static_routing_imix_asymmetric: *test_static_routing_imix
+
+
+test_ipv6_simple:
+ multiplier : 9
+ cores : 2
+ bw_per_core : 19.064
+
+
+test_nat_simple_mode1: &test_nat_simple
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+ multiplier : 6000
+ cores : 1
+ nat_opened : 500000
+ allow_timeout_dev : True
+ bw_per_core : 44.445
+
+test_nat_simple_mode2: *test_nat_simple
+
+test_nat_simple_mode3: *test_nat_simple
+
+test_nat_learning: *test_nat_simple
+
+
+test_nbar_simple:
+ multiplier : 7.5
+ cores : 2
+ bw_per_core : 17.174
+ nbar_classification:
+ http : 32.58
+ rtp-audio : 21.21
+ oracle_sqlnet : 11.41
+ exchange : 11.22
+ rtp : 11.2
+ citrix : 5.65
+ rtsp : 2.87
+ dns : 1.96
+ smtp : 0.57
+ pop3 : 0.37
+ ssl : 0.28
+ sctp : 0.13
+ sip : 0.09
+ unknown : 0.45
+
+
+test_rx_check_http: &rx_http
+ multiplier : 15000
+ cores : 1
+ rx_sample_rate : 16
+ bw_per_core : 39.560
+
+test_rx_check_http_ipv6:
+ << : *rx_http
+ bw_per_core : 49.237
+
+test_rx_check_http_negative_disabled:
+ << : *rx_http
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+
+
+test_rx_check_sfr: &rx_sfr
+ multiplier : 10
+ cores : 3
+ rx_sample_rate : 16
+ bw_per_core : 16.082
+
+test_rx_check_sfr_ipv6:
+ << : *rx_sfr
+ bw_per_core : 19.198
+
+
+
+### stateless ###
+
+test_CPU_benchmark:
+ profiles:
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# causes queue full
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 64, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# not enough memory + queue full if memory increase
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 9000, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/imix.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/pcap.py
+ kwargs : {ipg_usec: 2, loop_count: 0}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/hlt/hlt_udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+
diff --git a/scripts/automation/regression/setups/trex14/BU/config.yaml b/scripts/automation/regression/setups/trex14/BU/config.yaml
new file mode 100644
index 00000000..0fd6b70e
--- /dev/null
+++ b/scripts/automation/regression/setups/trex14/BU/config.yaml
@@ -0,0 +1,67 @@
+################################################################
+#### TRex nightly test configuration file ####
+################################################################
+
+
+### TRex configuration:
+# hostname - can be DNS name or IP for the TRex machine for ssh to the box
+# password - root password for TRex machine
+# is_dual - should the TRex inject with -p ?
+# version_path - path to the TRex version and executable
+# cores - how many cores should be used
+# latency - rate of latency packets injected by the TRex
+# modes - list of modes (tagging) of this setup (loopback etc.)
+# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
+# * VM - Virtual OS (accept low CPU utilization in tests, latency can get spikes)
+# * virt_nics - NICs are virtual (VMXNET3 etc.)
+
+### Router configuration:
+# hostname - the router hostname as apears in ______# cli prefix
+# ip_address - the router's ip that can be used to communicate with
+# image - the desired imaged wished to be loaded as the router's running config
+# line_password - router password when access via Telent
+# en_password - router password when changing to "enable" mode
+# interfaces - an array of client-server pairs, representing the interfaces configurations of the router
+# configurations - an array of configurations that could possibly loaded into the router during the test.
+# The "clean" configuration is a mandatory configuration the router will load with to run the basic test bench
+
+### TFTP configuration:
+# hostname - the tftp hostname
+# ip_address - the tftp's ip address
+# images_path - the tftp's relative path in which the router's images are located
+
+### Test_misc configuration:
+# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
+
+trex:
+ hostname : csi-trex-14
+ cores : 4
+ modes : []
+
+router:
+ model : ASR1001x
+ hostname : csi-asr-01
+ ip_address : 10.56.216.103
+ image : asr1001x-universalk9.03.17.00.S.156-1.S-std.SPA.bin
+ line_password : cisco
+ en_password : cisco
+ mgmt_interface : GigabitEthernet0
+ clean_config : /Configurations/danklei/asr1001_TRex_clean_config.cfg
+ intf_masking : 255.255.255.0
+ ipv6_mask : 64
+ interfaces :
+ - client :
+ name : Te0/0/0
+ src_mac_addr : 0000.0001.0000
+ dest_mac_addr : 0000.0001.0000
+ server :
+ name : Te0/0/1
+ src_mac_addr : 0000.0001.0000
+ dest_mac_addr : 0000.0001.0000
+ vrf_name : null
+
+tftp:
+ hostname : ats-asr-srv-1
+ ip_address : 10.56.217.7
+ root_dir : /scratch/tftp/
+ images_path : /asr1001x/
diff --git a/scripts/automation/regression/setups/trex14/benchmark.yaml b/scripts/automation/regression/setups/trex14/benchmark.yaml
index 04f13e79..0dc340b0 100644
--- a/scripts/automation/regression/setups/trex14/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex14/benchmark.yaml
@@ -75,20 +75,19 @@ test_nbar_simple:
cores : 2
bw_per_core : 17.174
nbar_classification:
- http : 32.58
- rtp-audio : 21.21
- oracle_sqlnet : 11.41
- exchange : 11.22
- rtp : 11.2
- citrix : 5.65
- rtsp : 2.87
- dns : 1.96
+ rtp : 32.57
+ http : 30.25
+ oracle_sqlnet : 11.23
+ exchange : 10.80
+ citrix : 5.62
+ rtsp : 2.84
+ dns : 1.95
smtp : 0.57
- pop3 : 0.37
- ssl : 0.28
+ pop3 : 0.36
+ ssl : 0.17
sctp : 0.13
sip : 0.09
- unknown : 0.45
+ unknown : 3.41
test_rx_check_http: &rx_http
diff --git a/scripts/automation/regression/setups/trex14/config.yaml b/scripts/automation/regression/setups/trex14/config.yaml
index 0fd6b70e..ffb61763 100644
--- a/scripts/automation/regression/setups/trex14/config.yaml
+++ b/scripts/automation/regression/setups/trex14/config.yaml
@@ -36,28 +36,27 @@
trex:
hostname : csi-trex-14
cores : 4
- modes : []
router:
model : ASR1001x
hostname : csi-asr-01
- ip_address : 10.56.216.103
- image : asr1001x-universalk9.03.17.00.S.156-1.S-std.SPA.bin
+ ip_address : 10.56.216.120
+ image : asr1001x-universalk9.03.13.02.S.154-3.S2-ext.SPA.bin
line_password : cisco
en_password : cisco
mgmt_interface : GigabitEthernet0
- clean_config : /Configurations/danklei/asr1001_TRex_clean_config.cfg
+ clean_config : clean_config.cfg
intf_masking : 255.255.255.0
ipv6_mask : 64
interfaces :
- client :
name : Te0/0/0
- src_mac_addr : 0000.0001.0000
- dest_mac_addr : 0000.0001.0000
+ src_mac_addr : 0000.0001.0002
+ dest_mac_addr : 0000.0001.0001
server :
name : Te0/0/1
- src_mac_addr : 0000.0001.0000
- dest_mac_addr : 0000.0001.0000
+ src_mac_addr : 0000.0002.0002
+ dest_mac_addr : 0000.0002.0001
vrf_name : null
tftp:
diff --git a/scripts/automation/regression/stateful_tests/trex_client_cfg_test.py b/scripts/automation/regression/stateful_tests/trex_client_cfg_test.py
new file mode 100644
index 00000000..852e745d
--- /dev/null
+++ b/scripts/automation/regression/stateful_tests/trex_client_cfg_test.py
@@ -0,0 +1,52 @@
+#!/router/bin/python
+from .trex_general_test import CTRexGeneral_Test, CTRexScenario
+from CPlatform import CStaticRouteConfig
+from .tests_exceptions import *
+#import sys
+import time
+from nose.tools import nottest
+
+# Testing client cfg ARP resolve. Actually, just need to check that TRex run finished with no errors.
+# If resolve will fail, TRex will exit with exit code != 0
+class CTRexClientCfg_Test(CTRexGeneral_Test):
+ """This class defines the IMIX testcase of the TRex traffic generator"""
+ def __init__(self, *args, **kwargs):
+ # super(CTRexClientCfg_Test, self).__init__()
+ CTRexGeneral_Test.__init__(self, *args, **kwargs)
+
+ def setUp(self):
+ if CTRexScenario.setup_name == 'kiwi02':
+ self.skip("Can't run currently on kiwi02")
+ super(CTRexClientCfg_Test, self).setUp() # launch super test class setUp process
+ pass
+
+ def test_client_cfg(self):
+ # test initializtion
+ if self.is_loopback:
+ return
+ else:
+ self.router.configure_basic_interfaces()
+ self.router.config_pbr(mode = "config")
+
+ ret = self.trex.start_trex(
+ c = 1,
+ m = 1,
+ d = 10,
+ f = 'cap2/dns.yaml',
+ v = 3,
+ client_cfg = 'automation/regression/cfg/client_cfg.yaml',
+ l = 1000)
+
+ trex_res = self.trex.sample_to_run_finish()
+
+ print("\nLATEST RESULT OBJECT:")
+ print(trex_res)
+
+ self.check_general_scenario_results(trex_res)
+
+ def tearDown(self):
+ CTRexGeneral_Test.tearDown(self)
+ pass
+
+if __name__ == "__main__":
+ pass
diff --git a/scripts/automation/regression/stateful_tests/trex_general_test.py b/scripts/automation/regression/stateful_tests/trex_general_test.py
index e968d380..fe38ed34 100755
--- a/scripts/automation/regression/stateful_tests/trex_general_test.py
+++ b/scripts/automation/regression/stateful_tests/trex_general_test.py
@@ -198,11 +198,14 @@ class CTRexGeneral_Test(unittest.TestCase):
def check_for_trex_crash(self):
pass
- def get_benchmark_param (self, param, sub_param = None, test_name = None):
+ def get_benchmark_param (self, param, sub_param = None, test_name = None,default=None):
if not test_name:
test_name = self.get_name()
if test_name not in self.benchmark:
- self.skip('No data in benchmark.yaml for test: %s, param: %s. Skipping.' % (test_name, param))
+ if default ==None:
+ self.skip('No data in benchmark.yaml for test: %s, param: %s. Skipping.' % (test_name, param))
+ else:
+ return default
if sub_param:
return self.benchmark[test_name][param].get(sub_param)
else:
@@ -254,7 +257,7 @@ class CTRexGeneral_Test(unittest.TestCase):
allowed_latency = 1000
if max(trex_res.get_max_latency().values()) > allowed_latency:
self.fail('LatencyError: Maximal latency exceeds %s (usec)' % allowed_latency)
-
+
# check that avg latency does not exceed 1 msec
if self.is_VM:
allowed_latency = 9999999
@@ -263,6 +266,15 @@ class CTRexGeneral_Test(unittest.TestCase):
if max(trex_res.get_avg_latency().values()) > allowed_latency:
self.fail('LatencyError: Average latency exceeds %s (usec)' % allowed_latency)
+ ports_names = trex_res.get_last_value('trex-latecny-v2.data', 'port\-\d+')
+ if not ports_names:
+ raise AbnormalResultError('Could not find ports info in TRex results, path: trex-latecny-v2.data.port-*')
+ for port_name in ports_names:
+ path = 'trex-latecny-v2.data.%s.hist.cnt' % port_name
+ lat_count = trex_res.get_last_value(path)
+ if lat_count == 0:
+ self.fail('LatencyError: Number of latency packets received on %s is 0' % port_name)
+
if not self.is_loopback:
# check router number of drops --> deliberately masked- need to be figured out!!!!!
pkt_drop_stats = self.router.get_drop_stats()
@@ -356,7 +368,7 @@ class CTRexGeneral_Test(unittest.TestCase):
print("Can't get TRex log:", e)
if len(self.fail_reasons):
sys.stdout.flush()
- raise Exception('The test is failed, reasons:\n%s' % '\n'.join(self.fail_reasons))
+ raise Exception('Test failed. Reasons:\n%s' % '\n'.join(self.fail_reasons))
sys.stdout.flush()
def check_for_trex_crash(self):
diff --git a/scripts/automation/regression/stateless_tests/stl_client_test.py b/scripts/automation/regression/stateless_tests/stl_client_test.py
index 36ac0ee1..73dac734 100644
--- a/scripts/automation/regression/stateless_tests/stl_client_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_client_test.py
@@ -240,10 +240,26 @@ class STLClient_Test(CStlGeneral_Test):
self.skip('skipping profile tests for virtual / non loopback')
return
+ default_mult = self.get_benchmark_param('mult',default="30%")
+ skip_tests = self.get_benchmark_param('skip',default=[])
+
try:
-
+ print("\n");
+
+
for profile in self.profiles:
+ skip=False
+ if skip_tests:
+ for skip_test in skip_tests:
+ if skip_test in profile:
+ skip=True;
+ break;
+ if skip:
+ print("skipping testing profile due to config file {0}...\n".format(profile))
+ continue;
+
+
print("now testing profile {0}...\n".format(profile))
p1 = STLProfile.load(profile, port_id = self.tx_port)
@@ -269,7 +285,7 @@ class STLClient_Test(CStlGeneral_Test):
self.c.clear_stats()
- self.c.start(ports = [self.tx_port, self.rx_port], mult = "30%")
+ self.c.start(ports = [self.tx_port, self.rx_port], mult = default_mult)
time.sleep(100 / 1000.0)
if p1.is_pauseable() and p2.is_pauseable():
diff --git a/scripts/automation/regression/stateless_tests/stl_performance_test.py b/scripts/automation/regression/stateless_tests/stl_performance_test.py
index a556daf3..641f0a33 100644
--- a/scripts/automation/regression/stateless_tests/stl_performance_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_performance_test.py
@@ -296,6 +296,10 @@ class STLPerformance_Test(CStlGeneral_Test):
# sample bps/pps
for _ in range(0, 20):
stats = self.c.get_stats(ports = 0)
+ if stats['global'][ 'queue_full']>10000:
+ assert 0, "Queue is full need to tune the multiplier"
+
+ # CPU results are not valid cannot use them
samples['bps'].append(stats[0]['tx_bps'])
samples['pps'].append(stats[0]['tx_pps'])
time.sleep(1)
diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py
index 524ad4bf..4dad712f 100644
--- a/scripts/automation/regression/stateless_tests/stl_rx_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py
@@ -51,6 +51,24 @@ class STLRX_Test(CStlGeneral_Test):
'latency_9k_enable': False,
'allow_packets_drop_num': 1, # allow 1 pkt drop
},
+
+ 'librte_pmd_mlx5': {
+ 'rate_percent': 80,
+ 'total_pkts': 1000,
+ 'rate_latency': 1,
+ 'latency_9k_enable': True,
+ 'latency_9k_max_average': 100,
+ 'latency_9k_max_latency': 250,
+ },
+
+ 'rte_enic_pmd': {
+ 'rate_percent': 1,
+ 'total_pkts': 50,
+ 'rate_latency': 1,
+ 'latency_9k_enable': False,
+ },
+
+
}
CStlGeneral_Test.setUp(self)
@@ -63,7 +81,6 @@ class STLRX_Test(CStlGeneral_Test):
port_info = self.c.get_port_info(ports = self.rx_port)[0]
self.speed = port_info['speed']
-
cap = port_info['rx']['caps']
if "flow_stats" not in cap or "latency" not in cap:
self.skip('port {0} does not support RX'.format(self.rx_port))
@@ -400,12 +417,14 @@ class STLRX_Test(CStlGeneral_Test):
s_port=random.sample(all_ports, random.randint(1, len(all_ports)) )
s_port=sorted(s_port)
- if self.speed == 40 :
+
+ if ((self.speed == 40) or (self.speed == 100)):
# the NIC does not support all full rate in case both port works let's filter odd ports
s_port=list(filter(lambda x: x % 2==0, s_port))
if len(s_port)==0:
s_port=[0];
+
error=1;
for j in range(0,5):
print(" {4} - duration {0} pgid {1} pkt_size {2} s_port {3} ".format(duration,pgid,pkt_size,s_port,j));
diff --git a/scripts/automation/regression/trex.py b/scripts/automation/regression/trex.py
index 7b96f2f8..416a6e3b 100644
--- a/scripts/automation/regression/trex.py
+++ b/scripts/automation/regression/trex.py
@@ -35,7 +35,7 @@ class CTRexScenario:
report_dir = 'reports'
# logger = None
test_types = {'functional_tests': [], 'stateful_tests': [], 'stateless_tests': []}
- is_copied = False
+ pkg_updated = False
GAManager = None
no_daemon = False
debug_image = False
diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py
index daa1abaf..09614770 100755
--- a/scripts/automation/regression/trex_unit_test.py
+++ b/scripts/automation/regression/trex_unit_test.py
@@ -137,35 +137,29 @@ class CTRexTestConfiguringPlugin(Plugin):
parser.add_option('--stf', '--stateful', action="store_true", default = False,
dest="stateful",
help="Run stateful tests.")
- parser.add_option('--pkg', action="store",
- dest="pkg",
- help="Run with given TRex package. Make sure the path available at server machine.")
+ parser.add_option('--pkg', type = str,
+ help="Run with given TRex package. Make sure the path available at server machine. Implies --restart-daemon.")
+ parser.add_option('--restart-daemon', action="store_true", default = False,
+ help="Flag that specifies to restart daemon. Implied by --pkg.")
parser.add_option('--collect', action="store_true", default = False,
- dest="collect",
help="Alias to --collect-only.")
parser.add_option('--warmup', action="store_true", default = False,
- dest="warmup",
help="Warm up the system for stateful: run 30 seconds 9k imix test without check of results.")
parser.add_option('--test-client-package', '--test_client_package', action="store_true", default = False,
- dest="test_client_package",
help="Includes tests of client package.")
parser.add_option('--long', action="store_true", default = False,
- dest="long",
help="Flag of long tests (stability).")
parser.add_option('--ga', action="store_true", default = False,
- dest="ga",
help="Flag to send benchmarks to GA.")
parser.add_option('--no-daemon', action="store_true", default = False,
dest="no_daemon",
help="Flag that specifies to use running stl server, no need daemons.")
parser.add_option('--debug-image', action="store_true", default = False,
- dest="debug_image",
help="Flag that specifies to use t-rex-64-debug as TRex executable.")
- parser.add_option('--trex-args', action='store', default = '',
- dest="trex_args",
+ parser.add_option('--trex-args', default = '',
help="Additional TRex arguments (--no-watchdog etc.).")
- parser.add_option('-t', '--test', action='store', default = '', dest='test',
- help='Test name to run (without file, class etc.)')
+ parser.add_option('-t', '--test', type = str,
+ help = 'Test name to run (without file, class etc.)')
def configure(self, options, conf):
@@ -174,10 +168,13 @@ class CTRexTestConfiguringPlugin(Plugin):
self.stateless = options.stateless
self.stateful = options.stateful
self.pkg = options.pkg
+ self.restart_daemon = options.restart_daemon
self.json_verbose = options.json_verbose
self.telnet_verbose = options.telnet_verbose
self.no_daemon = options.no_daemon
CTRexScenario.test = options.test
+ if self.no_daemon and (self.pkg or self.restart_daemon):
+ raise Exception('You have specified both --no-daemon and either --pkg or --restart-daemon at same time.')
if self.collect_only or self.functional:
return
if CTRexScenario.setup_dir and options.config_path:
@@ -212,6 +209,7 @@ class CTRexTestConfiguringPlugin(Plugin):
verbose = self.json_verbose,
debug_image = options.debug_image,
trex_args = options.trex_args)
+ if self.pkg or self.restart_daemon:
if not CTRexScenario.trex.check_master_connectivity():
print('Could not connect to master daemon')
sys.exit(-1)
@@ -228,20 +226,20 @@ class CTRexTestConfiguringPlugin(Plugin):
def begin (self):
client = CTRexScenario.trex
- if self.pkg and not CTRexScenario.is_copied:
+ if self.pkg and not CTRexScenario.pkg_updated:
if client.master_daemon.is_trex_daemon_running() and client.get_trex_cmds() and not self.kill_running:
- print("Can't update TRex, it's running")
+ print("Can't update TRex, it's running. Consider adding --kill-running flag.")
sys.exit(-1)
print('Updating TRex to %s' % self.pkg)
if not client.master_daemon.update_trex(self.pkg):
- print('Failed updating TRex')
+ print('Failed updating TRex.')
sys.exit(-1)
else:
- print('Updated')
- CTRexScenario.is_copied = True
+ print('Updated.')
+ CTRexScenario.pkg_updated = True
if self.functional or self.collect_only:
return
- if not self.no_daemon:
+ if self.pkg or self.restart_daemon:
print('Restarting TRex daemon server')
res = client.restart_trex_daemon()
if not res:
diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
index e9d2b8a0..5d992c6e 100755
--- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
+++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
@@ -150,10 +150,8 @@ class CTRexClient(object):
user = user or self.__default_user
try:
d = int(d)
- if d < 30 and not trex_development: # test duration should be at least 30 seconds, unless trex_development flag is specified.
- raise ValueError
except ValueError:
- raise ValueError('d parameter must be integer, specifying how long TRex run, and must be larger than 30 secs.')
+ raise ValueError('d parameter must be integer, specifying how long TRex run.')
trex_cmd_options.update( {'f' : f, 'd' : d} )
if not trex_cmd_options.get('l'):
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py
index b23b5f1f..627761ff 100755
--- a/scripts/automation/trex_control_plane/stl/console/trex_console.py
+++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py
@@ -202,7 +202,7 @@ class TRexConsole(TRexGeneralCmd):
func_name = f.__name__
if func_name.startswith("do_"):
func_name = func_name[3:]
-
+
if not inst.stateless_client.is_connected():
print(format_text("\n'{0}' cannot be executed on offline mode\n".format(func_name), 'bold'))
return
@@ -313,6 +313,7 @@ class TRexConsole(TRexGeneralCmd):
def do_shell (self, line):
self.do_history(line)
+ @verify_connected
def do_push (self, line):
'''Push a local PCAP file\n'''
self.stateless_client.push_line(line)
@@ -320,6 +321,7 @@ class TRexConsole(TRexGeneralCmd):
def help_push (self):
self.do_push("-h")
+ @verify_connected
def do_portattr (self, line):
'''Change/show port(s) attributes\n'''
self.stateless_client.set_port_attr_line(line)
@@ -328,6 +330,42 @@ class TRexConsole(TRexGeneralCmd):
self.do_portattr("-h")
@verify_connected
+ def do_l2 (self, line):
+ '''Configures a port in L2 mode'''
+ self.stateless_client.set_l2_mode_line(line)
+
+ def help_l2 (self):
+ self.do_l2("-h")
+
+ @verify_connected
+ def do_l3 (self, line):
+ '''Configures a port in L3 mode'''
+ self.stateless_client.set_l3_mode_line(line)
+
+ def help_l3 (self):
+ self.do_l3("-h")
+
+
+ @verify_connected
+ def do_set_rx_sniffer (self, line):
+ '''Sets a port sniffer on RX channel as PCAP recorder'''
+ self.stateless_client.set_rx_sniffer_line(line)
+
+ def help_sniffer (self):
+ self.do_set_rx_sniffer("-h")
+
+ @verify_connected
+ def do_resolve (self, line):
+ '''Resolve ARP for ports'''
+ self.stateless_client.resolve_line(line)
+
+ def help_resolve (self):
+ self.do_resolve("-h")
+
+ do_arp = do_resolve
+ help_arp = help_resolve
+
+ @verify_connected
def do_map (self, line):
'''Maps ports topology\n'''
ports = self.stateless_client.get_acquired_ports()
@@ -416,6 +454,7 @@ class TRexConsole(TRexGeneralCmd):
'''Release ports\n'''
self.stateless_client.release_line(line)
+ @verify_connected
def do_reacquire (self, line):
'''reacquire all the ports under your logged user name'''
self.stateless_client.reacquire_line(line)
@@ -469,7 +508,7 @@ class TRexConsole(TRexGeneralCmd):
############# update
@verify_connected
def do_update(self, line):
- '''update speed of port(s)currently transmitting traffic\n'''
+ '''update speed of port(s) currently transmitting traffic\n'''
self.stateless_client.update_line(line)
@@ -530,6 +569,13 @@ class TRexConsole(TRexGeneralCmd):
'''Clear cached local statistics\n'''
self.stateless_client.clear_stats_line(line)
+ @verify_connected
+ def do_service (self, line):
+ '''Sets port(s) service mode state'''
+ self.stateless_client.service_line(line)
+
+ def help_service (self, line):
+ self.do_service("-h")
def help_clear(self):
self.do_clear("-h")
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
index d7db6d30..37ef8000 100644
--- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py
+++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
@@ -645,14 +645,14 @@ class TrexTUI():
# regular state
if self.state == self.STATE_ACTIVE:
# if no connectivity - move to lost connecitivty
- if not self.stateless_client.async_client.is_alive():
+ if not self.stateless_client.async_client.is_active():
self.stateless_client._invalidate_stats(self.pm.ports)
self.state = self.STATE_LOST_CONT
# lost connectivity
elif self.state == self.STATE_LOST_CONT:
- # got it back
+ # if the async is alive (might be zomibe, but alive) try to reconnect
if self.stateless_client.async_client.is_alive():
# move to state reconnect
self.state = self.STATE_RECONNECT
@@ -1153,7 +1153,7 @@ class AsyncKeysEngineConsole:
# errors
else:
err_msgs = ascii_split(str(func_rc))
- self.last_status = format_text(err_msgs[0], 'red')
+ self.last_status = format_text(clear_formatting(err_msgs[0]), 'red')
if len(err_msgs) > 1:
self.last_status += " [{0} more errors messages]".format(len(err_msgs) - 1)
color = 'red'
diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir_update.py b/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir_update.py
new file mode 100644
index 00000000..22cceb8f
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir_update.py
@@ -0,0 +1,123 @@
+import stl_path
+from trex_stl_lib.api import *
+
+import imp
+import time
+import json
+from pprint import pprint
+import argparse
+
+# IMIX test
+# it maps the ports to sides
+# then it load a predefind profile 'IMIX'
+# and attach it to both sides and inject
+# at a certain rate for some time
+# finally it checks that all packets arrived
+def imix_test (server):
+
+
+ # create client
+ c = STLClient(server = server)
+ passed = True
+
+
+ try:
+
+ # connect to server
+ c.connect()
+
+ # take all the ports
+ c.reset()
+
+ dir_0 = [0]
+ dir_1 = [1]
+
+ print "Mapped ports to sides {0} <--> {1}".format(dir_0, dir_1)
+
+ # load IMIX profile
+ profile_file = os.path.join(stl_path.STL_PROFILES_PATH, 'imix.py')
+ profile1 = STLProfile.load_py(profile_file, direction=0)
+ profile2 = STLProfile.load_py(profile_file, direction=1)
+ stream1 = profile1.get_streams()
+ stream2 = profile2.get_streams()
+
+ # add both streams to ports
+ c.add_streams(stream1, ports = dir_0)
+ c.add_streams(stream2, ports = dir_1)
+
+
+ # clear the stats before injecting
+ c.clear_stats()
+
+ c.start(ports = (dir_0 + dir_1), mult = "100kpps", total = True)
+
+ while True:
+ for rate in range(200,3100,10):
+
+ # choose rate and start traffic for 10 seconds on 5 mpps
+ #mult = "30%"
+ my_mult = ("%dkpps"%rate)
+ print "Injecting {0} <--> {1} on total rate of '{2}' ".format(dir_0, dir_1, my_mult)
+ c.clear_stats()
+
+
+ c.update(ports = (dir_0 + dir_1), mult = my_mult)
+
+ #time.sleep(1);
+
+ # block until done
+ #c.wait_on_traffic(ports = (dir_0 + dir_1))
+
+ # read the stats after the test
+ stats = c.get_stats()
+
+ # use this for debug info on all the stats
+ pprint(stats)
+
+ # sum dir 0
+ dir_0_opackets = sum([stats[i]["opackets"] for i in dir_0])
+ dir_0_ipackets = sum([stats[i]["ipackets"] for i in dir_0])
+
+ # sum dir 1
+ dir_1_opackets = sum([stats[i]["opackets"] for i in dir_1])
+ dir_1_ipackets = sum([stats[i]["ipackets"] for i in dir_1])
+
+
+ lost_0 = dir_0_opackets - dir_1_ipackets
+ lost_1 = dir_1_opackets - dir_0_ipackets
+
+ print "\nPackets injected from {0}: {1:,}".format(dir_0, dir_0_opackets)
+ print "Packets injected from {0}: {1:,}".format(dir_1, dir_1_opackets)
+
+ print "\npackets lost from {0} --> {1}: {2:,} pkts".format(dir_0, dir_1, lost_0)
+ print "packets lost from {0} --> {1}: {2:,} pkts".format(dir_1, dir_0, lost_1)
+
+ if (lost_0 <= 0) and (lost_1 <= 0): # less or equal because we might have incoming arps etc.
+ passed = True
+ else:
+ passed = False
+
+
+ except STLError as e:
+ passed = False
+ print e
+
+ finally:
+ c.disconnect()
+
+ if passed:
+ print "\nTest has passed :-)\n"
+ else:
+ print "\nTest has failed :-(\n"
+
+parser = argparse.ArgumentParser(description="Example for TRex Stateless, sending IMIX traffic")
+parser.add_argument('-s', '--server',
+ dest='server',
+ help='Remote trex address',
+ default='127.0.0.1',
+ type = str)
+args = parser.parse_args()
+
+# run the tests
+imix_test(args.server)
+
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json b/scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json
new file mode 100644
index 00000000..85d10e65
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json
@@ -0,0 +1,269 @@
+{
+ "instructions": [
+ {
+ "id": "STLVmFlowVar",
+ "parameters": ["name", "init_value", "max_value","min_value","step", "size","op"]
+ },
+ {
+ "id": "STLVmWrFlowVar",
+ "parameters": ["fv_name", "pkt_offset","offset_fixup","add_val","is_big"]
+ },
+ {
+ "id": "STLVmWrMaskFlowVar",
+ "parameters": ["fv_name", "pkt_offset", "pkt_cast_size","mask", "shift","add_value","is_big"]
+ },
+ {
+ "id": "STLVmFixIpv4",
+ "parameters": ["offset"]
+ },
+ {
+ "id": "STLVmTrimPktSize",
+ "parameters": ["fv_name"]
+ },
+ {
+ "id": "STLVmTupleGen",
+ "parameters": ["name", "ip_min", "ip_max", "port_min", "port_max", "limit_flows", "flags"]
+ },
+ {
+ "id": "STLVmFlowVarRepetableRandom",
+ "parameters": ["name", "size", "limit", "seed", "min_value", "max_value"]
+ },
+ {
+ "id": "STLVmFixChecksumHw",
+ "parameters": ["l3_offset","l4_offset","l4_type"]
+ }
+ ],
+
+ "instruction_params_meta": [
+ {
+ "id": "name",
+ "name": "Name",
+ "type": "ENUM",
+ "editable": true,
+ "required": true,
+ "defaultValue": "Not defined"
+ },
+ {
+ "id": "init_value",
+ "name": "Initial value",
+ "type": "STRING",
+ "defaultValue": "0"
+ },
+ {
+ "id": "max_value",
+ "name": "Maximum value",
+ "type": "STRING",
+ "required": true,
+ "defaultValue": "0"
+ },
+ {
+ "id": "min_value",
+ "name": "Minimum value",
+ "type": "STRING",
+ "required": true,
+ "defaultValue": "0"
+ },
+ {
+ "id": "step",
+ "name": "Step",
+ "type": "NUMBER",
+ "required": true,
+ "defaultValue": "1"
+ },
+ {
+ "id": "op",
+ "name": "Operation",
+ "type": "ENUM",
+ "defaultValue": "inc",
+ "dict": {
+ "dec": "Decrement",
+ "inc": "Increment",
+ "random": "Random"
+ },
+ "required": true
+ },
+ {
+ "id": "size",
+ "name": "Size",
+ "type": "ENUM",
+ "defaultValue": "4",
+ "dict": {
+ "1": "1",
+ "2": "2",
+ "4": "4",
+ "8": "8"
+ }
+ },
+ {
+ "id": "fv_name",
+ "name": "Variable name",
+ "type": "ENUM",
+ "required": true,
+ "editable": true
+ },
+ {
+ "id": "pkt_offset",
+ "name": "Offset",
+ "type": "ENUM",
+ "required": true,
+ "editable": true,
+ "defaultValue": 0
+ },
+ {
+ "id": "pkt_cast_size",
+ "name": "Packet cast size",
+ "type": "ENUM",
+ "defaultValue": 1,
+ "dict":{
+ "1":1,
+ "2":2,
+ "4":4
+ }
+ },
+ {
+ "id": "shift",
+ "name": "Shift",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "mask",
+ "name": "Mask",
+ "type": "STRING",
+ "defaultValue": "0xff"
+ },
+ {
+ "id": "offset_fixup",
+ "name": "offset_fixup",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "add_val",
+ "name": "add_val",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "add_value",
+ "name": "add_value",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "is_big",
+ "name": "is_big",
+ "type": "ENUM",
+ "defaultValue": "true",
+ "dict": {
+ "true": "true",
+ "false": "false"
+ }
+ },
+ {
+ "id": "offset",
+ "name": "Offset",
+ "type": "ENUM",
+ "required": true,
+ "editable": true,
+ "defaultValue": 0
+ },
+ {
+ "id": "l3_offset",
+ "name": "L3 offset",
+ "type": "STRING",
+ "required": true,
+ "autocomplete": true,
+ "defaultValue": "IP"
+ },
+ {
+ "id": "l4_offset",
+ "name": "L4 offset",
+ "type": "STRING",
+ "required": true,
+ "defaultValue": "TCP"
+ },
+ {
+ "id": "ip_min",
+ "name": "Min IP",
+ "type": "STRING",
+ "defaultValue": "0.0.0.1"
+ },
+ {
+ "id": "ip_max",
+ "name": "Max IP",
+ "type": "STRING",
+ "defaultValue": "0.0.0.10"
+ },
+ {
+ "id": "port_max",
+ "name": "Max Port number",
+ "type": "NUMBER",
+ "defaultValue": 65535
+ },
+ {
+ "id": "port_min",
+ "name": "Min Port number",
+ "type": "NUMBER",
+ "defaultValue": 1025
+ },
+ {
+ "id": "limit_flows",
+ "name": "FLows limit",
+ "type": "NUMBER",
+ "defaultValue": 100000
+ },
+ {
+ "id": "limit",
+ "name": "Limit",
+ "type": "NUMBER",
+ "defaultValue": 100
+ },
+ {
+ "id": "seed",
+ "name": "Seed",
+ "type": "String",
+ "defaultValue": "None"
+ },
+ {
+ "id": "flags",
+ "name": "Flags",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "l4_type",
+ "name": "L4 type",
+ "type": "ENUM",
+ "required": true,
+ "editable": false,
+ "defaultValue": "13",
+ "dict": {
+ "11": "L4_TYPE_UDP",
+ "13": "L4_TYPE_TCP"
+ }
+ }
+ ],
+ "supported_protocols": ["IP","TCP","UDP"],
+ "templates":[
+ {
+ "id": "simple_flow_var",
+ "name": "Simple variable",
+ "instructionIds": ["STLVmFlowVar", "STLVmWrFlowVar"]
+ },
+ {
+ "id": "rep_rand_var",
+ "name": "Repeatable random",
+ "instructionIds": ["STLVmFlowVarRepetableRandom", "STLVmWrFlowVar"]
+ }
+ ],
+ "global_params_meta":[
+ {
+ "id": "cache_size",
+ "name": "Cache size",
+ "type": "NUMBER",
+ "defaultValue": "1000"
+ }
+ ]
+}
+
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py b/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py
index 8d99fe92..e5f1b20c 100755
--- a/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py
@@ -1,17 +1,20 @@
import os
import sys
+
stl_pathname = os.path.abspath(os.path.join(os.pardir, os.pardir))
sys.path.append(stl_pathname)
from trex_stl_lib.api import *
+import trex_stl_lib.trex_stl_packet_builder_scapy
import tempfile
import hashlib
import base64
import numbers
import random
-import inspect
+from inspect import getdoc
import json
+import re
from pprint import pprint
# add some layers as an example
@@ -121,6 +124,45 @@ class Scapy_service_api():
"""
pass
+ def build_pkt_ex(self, client_v_handler, pkt_model_descriptor, extra_options):
+ """ build_pkt_ex(self,client_v_handler,pkt_model_descriptor, extra_options) -> Dictionary (of Offsets,Show2 and Buffer)
+ Performs calculations on the given packet and returns results for that packet.
+
+ Parameters
+ ----------
+ pkt_descriptor - An array of dictionaries describing a network packet
+ extra_options - A dictionary of extra options required for building packet
+
+ Returns
+ -------
+ - The packets offsets: each field in every layer is mapped inside the Offsets Dictionary
+ - The Show2: A description of each field and its value in every layer of the packet
+ - The Buffer: The Hexdump of packet encoded in base64
+
+ Raises
+ ------
+ will raise an exception when the Scapy string format is illegal, contains syntax error, contains non-supported
+ protocl, etc.
+ """
+ pass
+
+ def load_instruction_parameter_values(self, client_v_handler, pkt_model_descriptor, vm_instructions_model, parameter_id):
+ """ load_instruction_parameter_values(self,client_v_handler,pkt_model_descriptor, vm_instructions_model, parameter_id) -> Dictionary (of possible parameter values)
+ Returns possible valies for given pararameter id depends on current pkt structure and vm_instructions
+ model.
+
+ Parameters
+ ----------
+ pkt_descriptor - An array of dictionaries describing a network packet
+ vm_instructions_model - A dictionary of extra options required for building packet
+ parameter_id - A string of parameter id
+
+ Returns
+ -------
+ Possible parameter values map.
+
+ """
+ pass
def get_tree(self,client_v_handler):
""" get_tree(self) -> Dictionary describing an example of hierarchy in layers
@@ -372,7 +414,15 @@ class Scapy_service(Scapy_service_api):
self.version_minor = '01'
self.server_v_hashed = self._generate_version_hash(self.version_major,self.version_minor)
self.protocol_definitions = {} # protocolId -> prococol definition overrides data
+ self.field_engine_supported_protocols = {}
+ self.instruction_parameter_meta_definitions = []
+ self.field_engine_parameter_meta_definitions = []
+ self.field_engine_templates_definitions = []
+ self.field_engine_instructions_meta = []
+ self.field_engine_instruction_expressions = []
self._load_definitions_from_json()
+ self._load_field_engine_meta_from_json()
+ self._vm_instructions = dict([m for m in inspect.getmembers(trex_stl_lib.trex_stl_packet_builder_scapy, inspect.isclass) if m[1].__module__ == 'trex_stl_lib.trex_stl_packet_builder_scapy'])
def _load_definitions_from_json(self):
# load protocol definitions from a json file
@@ -382,6 +432,27 @@ class Scapy_service(Scapy_service_api):
for protocol in protocols:
self.protocol_definitions[ protocol['id'] ] = protocol
+ def _load_field_engine_meta_from_json(self):
+ # load protocol definitions from a json file
+ self.instruction_parameter_meta_definitions = []
+ self.field_engine_supported_protocols = {}
+ self.field_engine_parameter_meta_definitions = []
+ self.field_engine_templates_definitions = []
+ with open('field_engine.json', 'r') as f:
+ metas = json.load(f)
+ self.instruction_parameter_meta_definitions = metas["instruction_params_meta"]
+ self.field_engine_instructions_meta = metas["instructions"]
+ self._append_intructions_help()
+ self.field_engine_supported_protocols = metas["supported_protocols"]
+ self.field_engine_parameter_meta_definitions = metas["global_params_meta"]
+ self.field_engine_templates_definitions = metas["templates"]
+
+
+ def _append_intructions_help(self):
+ for instruction_meta in self.field_engine_instructions_meta:
+ clazz = eval(instruction_meta['id'])
+ instruction_meta['help'] = base64.b64encode(getdoc(clazz.__init__)).decode('ascii')
+
def _all_protocol_structs(self):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
@@ -708,6 +779,134 @@ class Scapy_service(Scapy_service_api):
pkt = self._packet_model_to_scapy_packet(pkt_model_descriptor)
return self._pkt_data(pkt)
+
+ def build_pkt_ex(self, client_v_handler, pkt_model_descriptor, extra_options):
+ res = self.build_pkt(client_v_handler, pkt_model_descriptor)
+ pkt = self._packet_model_to_scapy_packet(pkt_model_descriptor)
+
+ field_engine = {}
+ field_engine['instructions'] = []
+ field_engine['error'] = None
+ try:
+ field_engine['instructions'] = self._generate_vm_instructions(pkt, extra_options['field_engine'])
+ except AssertionError as e:
+ field_engine['error'] = e.message
+ except CTRexPacketBuildException as e:
+ field_engine['error'] = e.message
+
+ field_engine['vm_instructions_expressions'] = self.field_engine_instruction_expressions
+ res['field_engine'] = field_engine
+ return res
+
+ def load_instruction_parameter_values(self, client_v_handler, pkt_model_descriptor, vm_instructions_model, parameter_id):
+
+ given_protocol_ids = [str(proto['id']) for proto in pkt_model_descriptor]
+
+ values = {}
+ if parameter_id == "name":
+ values = self._curent_pkt_protocol_fields(given_protocol_ids, "_")
+
+ if parameter_id == "fv_name":
+ values = self._existed_flow_var_names(vm_instructions_model['field_engine']['instructions'])
+
+ if parameter_id == "pkt_offset":
+ values = self._curent_pkt_protocol_fields(given_protocol_ids, ".")
+
+ if parameter_id == "offset":
+ for ip_idx in range(given_protocol_ids.count("IP")):
+ value = "IP:{0}".format(ip_idx)
+ values[value] = value
+
+ return {"map": values}
+
+ def _existed_flow_var_names(self, instructions):
+ return dict((instruction['parameters']['name'], instruction['parameters']['name']) for instruction in instructions if self._nameParamterExist(instruction))
+
+ def _nameParamterExist(self, instruction):
+ try:
+ instruction['parameters']['name']
+ return True
+ except KeyError:
+ return False
+
+ def _curent_pkt_protocol_fields(self, given_protocol_ids, delimiter):
+ given_protocol_classes = [c for c in Packet.__subclasses__() if c.__name__ in given_protocol_ids]
+ protocol_fields = {}
+ for protocol_class in given_protocol_classes:
+ protocol_name = protocol_class.__name__
+ protocol_count = given_protocol_ids.count(protocol_name)
+ for field_desc in protocol_class.fields_desc:
+ if delimiter == '.' and protocol_count > 1:
+ for idx in range(protocol_count):
+ formatted_name = "{0}:{1}{2}{3}".format(protocol_name, idx, delimiter, field_desc.name)
+ protocol_fields[formatted_name] = formatted_name
+ else:
+ formatted_name = "{0}{1}{2}".format(protocol_name, delimiter, field_desc.name)
+ protocol_fields[formatted_name] = formatted_name
+
+ return protocol_fields
+
+ def _generate_vm_instructions(self, pkt, field_engine_model_descriptor):
+ self.field_engine_instruction_expressions = []
+ instructions = []
+ instructions_def = field_engine_model_descriptor['instructions']
+ for instruction_def in instructions_def:
+ instruction_id = instruction_def['id']
+ instruction_class = self._vm_instructions[instruction_id]
+ parameters = {k: self._sanitize_value(k, v) for (k, v) in instruction_def['parameters'].iteritems()}
+ instructions.append(instruction_class(**parameters))
+
+ fe_parameters = field_engine_model_descriptor['global_parameters']
+
+ cache_size = None
+ if "cache_size" in fe_parameters:
+ assert self._is_int(fe_parameters['cache_size']), 'Cache size must be a number'
+ cache_size = int(fe_parameters['cache_size'])
+
+
+ pkt_builder = STLPktBuilder(pkt=pkt, vm=STLScVmRaw(instructions, cache_size=cache_size))
+ pkt_builder.compile()
+ return pkt_builder.get_vm_data()
+
+ def _sanitize_value(self, param_id, val):
+ if param_id == "pkt_offset":
+ if self._is_int(val):
+ return int(val)
+ elif val == "Ether.src":
+ return 0
+ elif val == "Ether.dst":
+ return 6
+ elif val == "Ether.type":
+ return 12
+ else:
+ if val == "None" or val == "none":
+ return None
+ if val == "true":
+ return True
+ elif val == "false":
+ return False
+ elif re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", str(val.lower())):
+ return int(str(val).replace(":", ""), 16)
+
+ if self._is_int(val):
+ return int(val)
+
+ str_val = str(val)
+ return int(str_val, 16) if str_val.startswith("0x") else str_val
+
+ def _get_instruction_parameter_meta(self, param_id):
+ for meta in self.instruction_parameter_meta_definitions:
+ if meta['id'] == param_id:
+ return meta
+ raise Scapy_Exception("Unable to get meta for {0}" % param_id)
+
+ def _is_int(self, val):
+ try:
+ int(val)
+ return True
+ except ValueError:
+ return False
+
# @deprecated. to be removed
def get_all(self,client_v_handler):
if not (self._verify_version_handler(client_v_handler)):
@@ -796,7 +995,11 @@ class Scapy_service(Scapy_service_api):
"name": protoDef.get('name') or pkt_class.name,
"fields": self._get_fields_definition(pkt_class, protoDef.get('fields') or [])
})
- res = {"protocols": protocols}
+ res = {"protocols": protocols,
+ "feInstructionParameters": self.instruction_parameter_meta_definitions,
+ "feInstructions": self.field_engine_instructions_meta,
+ "feParameters": self.field_engine_parameter_meta_definitions,
+ "feTemplates": self.field_engine_templates_definitions}
return res
def get_payload_classes(self,client_v_handler, pkt_model_descriptor):
@@ -889,8 +1092,6 @@ class Scapy_service(Scapy_service_api):
pcap_bin = tmpPcap.read()
return bytes_to_b64(pcap_bin)
-
-
#---------------------------------------------------------------------------
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py
index 1db2c62b..e48880e8 100644
--- a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py
@@ -53,6 +53,9 @@ def get_version_handler():
def build_pkt(model_def):
return pass_result(service.build_pkt(v_handler, model_def))
+def build_pkt_ex(model_def, instructions_def):
+ return pass_result(service.build_pkt_ex(v_handler, model_def, instructions_def))
+
def build_pkt_get_scapy(model_def):
return build_pkt_to_scapy(build_pkt(model_def))
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py
index 91a457dc..e1094a79 100644
--- a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py
@@ -113,15 +113,23 @@ def test_get_all():
def test_get_definitions_all():
get_definitions(None)
- def_classnames = [pdef['id'] for pdef in get_definitions(None)['protocols']]
+ defs = get_definitions(None)
+ def_classnames = [pdef['id'] for pdef in defs['protocols']]
assert("IP" in def_classnames)
assert("Dot1Q" in def_classnames)
assert("TCP" in def_classnames)
+ # All instructions should have a help description.
+ fe_instructions = defs['feInstructions']
+ for instruction in fe_instructions:
+ print(instruction['help'])
+ assert("help" in instruction)
+
def test_get_definitions_ether():
res = get_definitions(["Ether"])
- assert(len(res) == 1)
- assert(res['protocols'][0]['id'] == "Ether")
+ protocols = res['protocols']
+ assert(len(protocols) == 1)
+ assert(protocols[0]['id'] == "Ether")
def test_get_payload_classes():
eth_payloads = get_payload_classes([{"id":"Ether"}])
@@ -250,3 +258,34 @@ def test_ip_definitions():
assert(fields[9]['id'] == 'chksum')
assert(fields[9]['auto'] == True)
+def test_generate_vm_instructions():
+ ip_pkt_model = [
+ layer_def("Ether"),
+ layer_def("IP", src="16.0.0.1", dst="48.0.0.1")
+ ]
+ ip_instructions_model = {"field_engine": {"instructions": [{"id": "STLVmFlowVar",
+ "parameters": {"op": "inc", "min_value": "192.168.0.10",
+ "size": "1", "name": "ip_src",
+ "step": "1",
+ "max_value": "192.168.0.100"}},
+ {"id": "STLVmWrFlowVar",
+ "parameters": {"pkt_offset": "IP.src", "is_big": "true",
+ "add_val": "0", "offset_fixup": "0",
+ "fv_name": "ip_src"}},
+ {"id": "STLVmFlowVar",
+ "parameters": {"op": "dec", "min_value": "32",
+ "size": "1", "name": "ip_ttl",
+ "step": "4", "max_value": "64"}},
+ {"id": "STLVmWrFlowVar",
+ "parameters": {"pkt_offset": "IP.ttl", "is_big": "true",
+ "add_val": "0", "offset_fixup": "0",
+ "fv_name": "ip_ttl"}}],
+ "global_parameters": {}}}
+ res = build_pkt_ex(ip_pkt_model, ip_instructions_model)
+ src_instruction = res['field_engine']['instructions']['instructions'][0]
+ assert(src_instruction['min_value'] == 3232235530)
+ assert(src_instruction['max_value'] == 3232235620)
+
+ ttl_instruction = res['field_engine']['instructions']['instructions'][2]
+ assert(ttl_instruction['min_value'] == 32)
+ assert(ttl_instruction['max_value'] == 64)
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
index 2c95844b..11e87592 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
@@ -137,6 +137,10 @@ class CTRexAsyncStatsManager():
class CTRexAsyncClient():
+ THREAD_STATE_ACTIVE = 1
+ THREAD_STATE_ZOMBIE = 2
+ THREAD_STATE_DEAD = 3
+
def __init__ (self, server, port, stateless_client):
self.port = port
@@ -159,7 +163,10 @@ class CTRexAsyncClient():
self.connected = False
self.zipped = ZippedMsg()
-
+
+ self.t_state = self.THREAD_STATE_DEAD
+
+
# connects the async channel
def connect (self):
@@ -173,8 +180,8 @@ class CTRexAsyncClient():
self.socket = self.context.socket(zmq.SUB)
- # before running the thread - mark as active
- self.active = True
+ # before running the thread - mark as active
+ self.t_state = self.THREAD_STATE_ACTIVE
self.t = threading.Thread(target = self._run)
# kill this thread on exit and don't add it to the join list
@@ -198,26 +205,26 @@ class CTRexAsyncClient():
return RC_OK()
-
-
# disconnect
def disconnect (self):
if not self.connected:
return
# mark for join
- self.active = False
-
- # signal that the context was destroyed (exit the thread loop)
+ self.t_state = self.THREAD_STATE_DEAD
self.context.term()
-
- # join
self.t.join()
+
# done
self.connected = False
+ # set the thread as a zombie (in case of server death)
+ def set_as_zombie (self):
+ self.last_data_recv_ts = None
+ self.t_state = self.THREAD_STATE_ZOMBIE
+
# thread function
def _run (self):
@@ -231,12 +238,19 @@ class CTRexAsyncClient():
self.monitor.reset()
- while self.active:
+ while self.t_state != self.THREAD_STATE_DEAD:
try:
with self.monitor:
line = self.socket.recv()
+ # last data recv.
+ self.last_data_recv_ts = time.time()
+
+ # if thread was marked as zomibe - it does nothing besides fetching messages
+ if self.t_state == self.THREAD_STATE_ZOMBIE:
+ continue
+
self.monitor.on_recv_msg(line)
# try to decomrpess
@@ -246,7 +260,6 @@ class CTRexAsyncClient():
line = line.decode()
- self.last_data_recv_ts = time.time()
# signal once
if not got_data:
@@ -259,13 +272,14 @@ class CTRexAsyncClient():
# signal once
if got_data:
self.event_handler.on_async_dead()
+ self.set_as_zombie()
got_data = False
continue
except zmq.ContextTerminated:
# outside thread signaled us to exit
- assert(not self.active)
+ assert(self.t_state != self.THREAD_STATE_ACTIVE)
break
msg = json.loads(line)
@@ -283,16 +297,29 @@ class CTRexAsyncClient():
# closing of socket must be from the same thread
self.socket.close(linger = 0)
- def is_thread_alive (self):
- return self.t.is_alive()
-
- # did we get info for the last 3 seconds ?
+
+ # return True if the subscriber got data in the last 3 seconds
+ # even if zombie - will return true if got data
def is_alive (self):
+
+ # maybe the thread has exited with exception
+ if not self.t.is_alive():
+ return False
+
+ # simply no data
if self.last_data_recv_ts == None:
return False
+ # timeout of data
return ( (time.time() - self.last_data_recv_ts) < 3 )
+
+ # more granular than active - it means that thread state is active we get info
+ # zomibes will return false
+ def is_active (self):
+ return self.is_alive() and self.t_state == self.THREAD_STATE_ACTIVE
+
+
def get_stats (self):
return self.stats
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
index 80a4c4dc..a42247e7 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
@@ -12,9 +12,10 @@ from .trex_stl_types import *
from .trex_stl_async_client import CTRexAsyncClient
from .utils import parsing_opts, text_tables, common
-from .utils.common import list_intersect, list_difference, is_sub_list, PassiveTimer
+from .utils.common import *
from .utils.text_opts import *
from functools import wraps
+from texttable import ansi_len
from collections import namedtuple
from yaml import YAMLError
@@ -24,6 +25,8 @@ import re
import random
import json
import traceback
+import os.path
+
############################ logger #############################
############################ #############################
@@ -32,9 +35,10 @@ import traceback
# logger API for the client
class LoggerApi(object):
# verbose levels
- VERBOSE_QUIET = 0
- VERBOSE_REGULAR = 1
- VERBOSE_HIGH = 2
+ VERBOSE_QUIET = 0
+ VERBOSE_REGULAR_SYNC = 1
+ VERBOSE_REGULAR = 2
+ VERBOSE_HIGH = 3
def __init__(self):
self.level = LoggerApi.VERBOSE_REGULAR
@@ -62,7 +66,7 @@ class LoggerApi(object):
# simple log message with verbose
- def log (self, msg, level = VERBOSE_REGULAR, newline = True):
+ def log (self, msg, level = VERBOSE_REGULAR_SYNC, newline = True):
if not self.check_verbose(level):
return
@@ -90,19 +94,21 @@ class LoggerApi(object):
# supress object getter
- def supress (self):
+ def supress (self, level = VERBOSE_QUIET):
class Supress(object):
- def __init__ (self, logger):
+ def __init__ (self, logger, level):
self.logger = logger
+ self.level = level
def __enter__ (self):
self.saved_level = self.logger.get_verbose()
- self.logger.set_verbose(LoggerApi.VERBOSE_QUIET)
+ if self.level < self.saved_level:
+ self.logger.set_verbose(self.level)
def __exit__ (self, type, value, traceback):
self.logger.set_verbose(self.saved_level)
- return Supress(self)
+ return Supress(self, level)
@@ -175,8 +181,8 @@ class EventsHandler(object):
def on_async_dead (self):
if self.client.connected:
msg = 'Lost connection to server'
- self.__add_event_log('local', 'info', msg, True)
self.client.connected = False
+ self.__add_event_log('local', 'info', msg, True)
def on_async_alive (self):
@@ -322,25 +328,30 @@ class EventsHandler(object):
# port attr changed
elif (event_type == 8):
+
port_id = int(data['port_id'])
- if data['attr'] == self.client.ports[port_id].attr:
- return # false alarm
- old_info = self.client.ports[port_id].get_info()
- self.__async_event_port_attr_changed(port_id, data['attr'])
- new_info = self.client.ports[port_id].get_info()
+
+ diff = self.__async_event_port_attr_changed(port_id, data['attr'])
+ if not diff:
+ return
+
+
ev = "port {0} attributes changed".format(port_id)
- for key, old_val in old_info.items():
- new_val = new_info[key]
- if old_val != new_val:
- ev += '\n {key}: {old} -> {new}'.format(
- key = key,
- old = old_val.lower() if type(old_val) is str else old_val,
- new = new_val.lower() if type(new_val) is str else new_val)
+ for key, (old_val, new_val) in diff.items():
+ ev += '\n {key}: {old} -> {new}'.format(
+ key = key,
+ old = old_val.lower() if type(old_val) is str else old_val,
+ new = new_val.lower() if type(new_val) is str else new_val)
+
show_event = True
-
+
+
+
# server stopped
elif (event_type == 100):
ev = "Server has stopped"
+ # to avoid any new messages on async
+ self.client.async_client.set_as_zombie()
self.__async_event_server_stopped()
show_event = True
@@ -392,7 +403,7 @@ class EventsHandler(object):
def __async_event_port_attr_changed (self, port_id, attr):
if port_id in self.client.ports:
- self.client.ports[port_id].async_event_port_attr_changed(attr)
+ return self.client.ports[port_id].async_event_port_attr_changed(attr)
# add event to log
def __add_event_log (self, origin, ev_type, msg, show = False):
@@ -650,7 +661,7 @@ class STLClient(object):
return rc
-
+
def __add_streams(self, stream_list, port_id_list = None):
port_id_list = self.__ports(port_id_list)
@@ -767,7 +778,7 @@ class STLClient(object):
return rc
- def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count, duration, is_dual):
+ def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count, duration, is_dual, min_ipg_usec):
port_id_list = self.__ports(port_id_list)
rc = RC()
@@ -783,7 +794,8 @@ class STLClient(object):
count,
duration,
is_dual,
- slave_handler))
+ slave_handler,
+ min_ipg_usec))
return rc
@@ -799,17 +811,85 @@ class STLClient(object):
return rc
+ def __resolve (self, port_id_list = None, retries = 0):
+ port_id_list = self.__ports(port_id_list)
+
+ rc = RC()
+
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].arp_resolve(retries))
+
+ return rc
+
+
def __set_port_attr (self, port_id_list = None, attr_dict = None):
port_id_list = self.__ports(port_id_list)
rc = RC()
+ for port_id, port_attr_dict in zip(port_id_list, attr_dict):
+ rc.add(self.ports[port_id].set_attr(**port_attr_dict))
+
+ return rc
+
+
+ def __set_rx_sniffer (self, port_id_list, base_filename, limit):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
+ for port_id in port_id_list:
+ head, tail = os.path.splitext(base_filename)
+ filename = "{0}-{1}{2}".format(head, port_id, tail)
+ rc.add(self.ports[port_id].set_rx_sniffer(filename, limit))
+
+ return rc
+
+
+ def __remove_rx_sniffer (self, port_id_list):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
for port_id in port_id_list:
- rc.add(self.ports[port_id].set_attr(attr_dict))
+ rc.add(self.ports[port_id].remove_rx_sniffer())
return rc
+ def __set_rx_queue (self, port_id_list, size):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].set_rx_queue(size))
+
+ return rc
+
+ def __remove_rx_queue (self, port_id_list):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].remove_rx_queue())
+
+ return rc
+
+ def __get_rx_queue_pkts (self, port_id_list):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].get_rx_queue_pkts())
+
+ return rc
+
+ def __set_service_mode (self, port_id_list, enabled):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].set_service_mode(enabled))
+
+ return rc
+
# connect to server
def __connect(self):
@@ -1008,7 +1088,7 @@ class STLClient(object):
if not port_id in valid_ports:
raise STLError("Port ID '{0}' is not a valid port ID - valid values: {1}".format(port_id, valid_ports))
- return port_id_list
+ return list_remove_dup(port_id_list)
# transmit request on the RPC link
@@ -1313,6 +1393,18 @@ class STLClient(object):
if port_obj.is_active()]
+ def get_resolvable_ports (self):
+ return [port_id
+ for port_id, port_obj in self.ports.items()
+ if port_obj.is_acquired() and port_obj.get_dst_addr()['ipv4'] is not None]
+
+
+ def get_service_enabled_ports(self):
+ return [port_id
+ for port_id, port_obj in self.ports.items()
+ if port_obj.is_acquired() and port_obj.is_service_mode_on()]
+
+
# get paused ports
def get_paused_ports (self, owned = True):
if owned:
@@ -1338,6 +1430,7 @@ class STLClient(object):
# get stats
+ @__api_check(True)
def get_stats (self, ports = None, sync_now = True):
"""
Return dictionary containing statistics information gathered from the server.
@@ -1585,7 +1678,7 @@ class STLClient(object):
ports = ports if ports is not None else self.get_all_ports()
ports = self._validate_port_list(ports)
- return [self.ports[port_id].get_info() for port_id in ports]
+ return [self.ports[port_id].get_formatted_info() for port_id in ports]
############################ Commands #############################
@@ -1700,6 +1793,9 @@ class STLClient(object):
self.__release(ports)
raise STLError(rc)
+ for port_id in ports:
+ if not self.ports[port_id].is_resolved():
+ self.logger.log(format_text('*** Warning - Port {0} destination is unresolved ***'.format(port_id), 'bold'))
@__api_check(True)
def release (self, ports = None):
@@ -1725,30 +1821,144 @@ class STLClient(object):
if not rc:
raise STLError(rc)
+
+
@__api_check(True)
- def ping(self):
+ def ping_rpc_server(self):
"""
- Pings the server
+ Pings the RPC server
:parameters:
- None
-
+ None
:raises:
+ :exc:`STLError`
"""
-
+
self.logger.pre_cmd("Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'],
self.connection_info['sync_port']))
rc = self._transmit("ping", api_class = None)
+
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+ @__api_check(True)
+ def set_l2_mode (self, port, dst_mac):
+ """
+ Sets the port mode to L2
+
+ :parameters:
+ port - the port to set the source address
+ dst_mac - destination MAC
+ :raises:
+ + :exc:`STLError`
+ """
+ validate_type('port', port, int)
+ if port not in self.get_all_ports():
+ raise STLError("port {0} is not a valid port id".format(port))
+
+ if not is_valid_mac(dst_mac):
+ raise STLError("dest_mac is not a valid MAC address: '{0}'".format(dst_mac))
+
+ self.logger.pre_cmd("Setting port {0} in L2 mode: ".format(port))
+ rc = self.ports[port].set_l2_mode(dst_mac)
self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+ @__api_check(True)
+ def set_l3_mode (self, port, src_ipv4, dst_ipv4):
+ """
+ Sets the port mode to L3
+ :parameters:
+ port - the port to set the source address
+ src_ipv4 - IPv4 source address for the port
+ dst_ipv4 - IPv4 destination address
+ :raises:
+ + :exc:`STLError`
+ """
+
+ validate_type('port', port, int)
+ if port not in self.get_all_ports():
+ raise STLError("port {0} is not a valid port id".format(port))
+
+ if not is_valid_ipv4(src_ipv4):
+ raise STLError("src_ipv4 is not a valid IPv4 address: '{0}'".format(src_ipv4))
+
+ if not is_valid_ipv4(dst_ipv4):
+ raise STLError("dst_ipv4 is not a valid IPv4 address: '{0}'".format(dst_ipv4))
+
+ self.logger.pre_cmd("Setting port {0} in L3 mode: ".format(port))
+ rc = self.ports[port].set_l3_mode(src_ipv4, dst_ipv4)
+ self.logger.post_cmd(rc)
+
if not rc:
raise STLError(rc)
+
+ # try to resolve
+ with self.logger.supress(level = LoggerApi.VERBOSE_REGULAR_SYNC):
+ self.logger.pre_cmd("ARP resolving address '{0}': ".format(dst_ipv4))
+ rc = self.ports[port].arp_resolve(0)
+ self.logger.post_cmd(rc)
+ if not rc:
+ raise STLError(rc)
+
@__api_check(True)
+ def ping_ip (self, src_port, dst_ipv4, pkt_size = 64, count = 5):
+ """
+ Pings an IP address through a port
+
+ :parameters:
+ src_port - on which port_id to send the ICMP PING request
+ dst_ipv4 - which IP to ping
+ pkt_size - packet size to use
+ count - how many times to ping
+ :raises:
+ + :exc:`STLError`
+
+ """
+ # validate src port
+ validate_type('src_port', src_port, int)
+ if src_port not in self.get_all_ports():
+ raise STLError("src port is not a valid port id")
+
+ if not is_valid_ipv4(dst_ipv4):
+ raise STLError("dst_ipv4 is not a valid IPv4 address: '{0}'".format(dst_ipv4))
+
+ if (pkt_size < 64) or (pkt_size > 9216):
+ raise STLError("pkt_size should be a value between 64 and 9216: '{0}'".format(pkt_size))
+
+ validate_type('count', count, int)
+
+ self.logger.pre_cmd("Pinging {0} from port {1} with {2} bytes of data:".format(dst_ipv4,
+ src_port,
+ pkt_size))
+
+ # no async messages
+ with self.logger.supress(level = LoggerApi.VERBOSE_REGULAR_SYNC):
+ self.logger.log('')
+ for i in range(count):
+ rc = self.ports[src_port].ping(ping_ipv4 = dst_ipv4, pkt_size = pkt_size)
+ if not rc:
+ raise STLError(rc)
+
+ self.logger.log(rc.data())
+
+ if i != (count - 1):
+ time.sleep(1)
+
+
+
+ @__api_check(True)
def server_shutdown (self, force = False):
"""
Sends the server a request for total shutdown
@@ -1795,6 +2005,8 @@ class STLClient(object):
if not rc:
raise STLError(rc)
+ return rc.data()
+
@__api_check(True)
def get_util_stats(self):
"""
@@ -1814,7 +2026,6 @@ class STLClient(object):
@__api_check(True)
def get_xstats(self, port_id):
- print(port_id)
"""
Get extended stats of port: all the counters as dict.
@@ -1833,7 +2044,7 @@ class STLClient(object):
@__api_check(True)
- def reset(self, ports = None):
+ def reset(self, ports = None, restart = False):
"""
Force acquire ports, stop the traffic, remove all streams and clear stats
@@ -1841,7 +2052,9 @@ class STLClient(object):
ports : list
Ports on which to execute the command
-
+ restart: bool
+ Restart the NICs (link down / up)
+
:raises:
+ :exc:`STLError`
@@ -1851,12 +2064,34 @@ class STLClient(object):
ports = ports if ports is not None else self.get_all_ports()
ports = self._validate_port_list(ports)
- # force take the port and ignore any streams on it
- self.acquire(ports, force = True, sync_streams = False)
- self.stop(ports, rx_delay_ms = 0)
- self.remove_all_streams(ports)
- self.clear_stats(ports)
+
+ if restart:
+ self.logger.pre_cmd("Hard resetting ports {0}:".format(ports))
+ else:
+ self.logger.pre_cmd("Resetting ports {0}:".format(ports))
+
+
+ try:
+ with self.logger.supress():
+ # force take the port and ignore any streams on it
+ self.acquire(ports, force = True, sync_streams = False)
+ self.stop(ports, rx_delay_ms = 0)
+ self.remove_all_streams(ports)
+ self.clear_stats(ports)
+ self.set_port_attr(ports,
+ promiscuous = False,
+ link_up = True if restart else None)
+ self.set_service_mode(ports, False)
+ self.remove_rx_sniffer(ports)
+ self.remove_rx_queue(ports)
+
+ except STLError as e:
+ self.logger.post_cmd(False)
+ raise e
+
+ self.logger.post_cmd(RC_OK())
+
@__api_check(True)
def remove_all_streams (self, ports = None):
@@ -1995,7 +2230,28 @@ class STLClient(object):
raise STLError(rc)
-
+ # common checks for start API
+ def __pre_start_check (self, ports, force):
+
+ # verify link status
+ ports_link_down = [port_id for port_id in ports if not self.ports[port_id].is_up()]
+ if ports_link_down and not force:
+ raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down)
+
+ # verify ports are stopped or force stop them
+ active_ports = [port_id for port_id in ports if self.ports[port_id].is_active()]
+ if active_ports and not force:
+ raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports))
+
+ # warn if ports are not resolved
+ unresolved_ports = [port_id for port_id in ports if not self.ports[port_id].is_resolved()]
+ if unresolved_ports and not force:
+ raise STLError("Port(s) {0} have unresolved destination addresses - please resolve them or specify 'force'".format(unresolved_ports))
+
+ if self.get_service_enabled_ports() and not force:
+ raise STLError("Port(s) {0} are under service mode - please disable service mode or specify 'force'".format(self.get_service_enabled_ports()))
+
+
@__api_check(True)
def start (self,
ports = None,
@@ -2050,11 +2306,10 @@ class STLClient(object):
validate_type('total', total, bool)
validate_type('core_mask', core_mask, (int, list))
- # verify link status
- ports_link_down = [port_id for port_id in ports if self.ports[port_id].attr.get('link',{}).get('up') == False]
- if not force and ports_link_down:
- raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down)
-
+
+ # some sanity checks before attempting start
+ self.__pre_start_check(ports, force)
+
#########################
# decode core mask argument
decoded_mask = self.__decode_core_mask(ports, core_mask)
@@ -2068,17 +2323,12 @@ class STLClient(object):
raise STLArgumentError('mult', mult)
- # verify ports are stopped or force stop them
+ # stop active ports if needed
active_ports = list(set(self.get_active_ports()).intersection(ports))
- if active_ports:
- if not force:
- raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports))
- else:
- rc = self.stop(active_ports)
- if not rc:
- raise STLError(rc)
-
+ if active_ports and force:
+ self.stop(active_ports)
+
# start traffic
self.logger.pre_cmd("Starting traffic on port(s) {0}:".format(ports))
rc = self.__start(mult_obj, duration, ports, force, decoded_mask)
@@ -2115,7 +2365,7 @@ class STLClient(object):
return
ports = self._validate_port_list(ports)
-
+
self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(ports))
rc = self.__stop(ports)
self.logger.post_cmd(rc)
@@ -2245,7 +2495,8 @@ class STLClient(object):
speedup = 1.0,
count = 1,
duration = -1,
- is_dual = False):
+ is_dual = False,
+ min_ipg_usec = None):
"""
Push a remote server-reachable PCAP file
the path must be fullpath accessible to the server
@@ -2258,7 +2509,8 @@ class STLClient(object):
Ports on which to execute the command
ipg_usec : float
- Inter-packet gap in microseconds
+ Inter-packet gap in microseconds.
+ Exclusive with min_ipg_usec
speedup : float
A factor to adjust IPG. effectively IPG = IPG / speedup
@@ -2275,6 +2527,10 @@ class STLClient(object):
also requires that all the ports will be in master mode
with their adjacent ports as slaves
+ min_ipg_usec : float
+ Minimum inter-packet gap in microseconds to guard from too small ipg.
+ Exclusive with ipg_usec
+
:raises:
+ :exc:`STLError`
@@ -2288,6 +2544,7 @@ class STLClient(object):
validate_type('count', count, int)
validate_type('duration', duration, (float, int))
validate_type('is_dual', is_dual, bool)
+ validate_type('min_ipg_usec', min_ipg_usec, (float, int, type(None)))
# for dual mode check that all are masters
if is_dual:
@@ -2306,7 +2563,7 @@ class STLClient(object):
self.logger.pre_cmd("Pushing remote PCAP on port(s) {0}:".format(ports))
- rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count, duration, is_dual)
+ rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count, duration, is_dual, min_ipg_usec)
self.logger.post_cmd(rc)
if not rc:
@@ -2324,7 +2581,8 @@ class STLClient(object):
force = False,
vm = None,
packet_hook = None,
- is_dual = False):
+ is_dual = False,
+ min_ipg_usec = None):
"""
Push a local PCAP to the server
This is equivalent to loading a PCAP file to a profile
@@ -2340,7 +2598,8 @@ class STLClient(object):
Ports on which to execute the command
ipg_usec : float
- Inter-packet gap in microseconds
+ Inter-packet gap in microseconds.
+ Exclusive with min_ipg_usec
speedup : float
A factor to adjust IPG. effectively IPG = IPG / speedup
@@ -2366,6 +2625,10 @@ class STLClient(object):
also requires that all the ports will be in master mode
with their adjacent ports as slaves
+ min_ipg_usec : float
+ Minimum inter-packet gap in microseconds to guard from too small ipg.
+ Exclusive with ipg_usec
+
:raises:
+ :exc:`STLError`
@@ -2380,6 +2643,9 @@ class STLClient(object):
validate_type('duration', duration, (float, int))
validate_type('vm', vm, (list, type(None)))
validate_type('is_dual', is_dual, bool)
+ validate_type('min_ipg_usec', min_ipg_usec, (float, int, type(None)))
+ if all([ipg_usec, min_ipg_usec]):
+ raise STLError('Please specify either ipg or minimal ipg, not both.')
# no support for > 1MB PCAP - use push remote
@@ -2392,7 +2658,7 @@ class STLClient(object):
slave = port ^ 0x1
if slave in ports:
- raise STLError("dual mode: cannot provide adjacent ports ({0}, {1}) in a batch".format(master, slave))
+ raise STLError("dual mode: please specify only one of adjacent ports ({0}, {1}) in a batch".format(master, slave))
if not slave in self.get_acquired_ports():
raise STLError("dual mode: adjacent port {0} must be owned during dual mode".format(slave))
@@ -2408,7 +2674,8 @@ class STLClient(object):
speedup,
count,
vm = vm,
- packet_hook = packet_hook)
+ packet_hook = packet_hook,
+ min_ipg_usec = min_ipg_usec)
self.logger.post_cmd(RC_OK)
except STLError as e:
self.logger.post_cmd(RC_ERR(e))
@@ -2433,7 +2700,8 @@ class STLClient(object):
count,
vm = vm,
packet_hook = packet_hook,
- split_mode = split_mode)
+ split_mode = split_mode,
+ min_ipg_usec = min_ipg_usec)
self.logger.post_cmd(RC_OK())
@@ -2441,7 +2709,7 @@ class STLClient(object):
self.logger.post_cmd(RC_ERR(e))
raise
- all_ports = ports + [p ^ 0x1 for p in ports]
+ all_ports = ports + [p ^ 0x1 for p in ports if profile_b]
self.remove_all_streams(ports = all_ports)
@@ -2450,7 +2718,8 @@ class STLClient(object):
slave = port ^ 0x1
self.add_streams(profile_a.get_streams(), master)
- self.add_streams(profile_b.get_streams(), slave)
+ if profile_b:
+ self.add_streams(profile_b.get_streams(), slave)
return self.start(ports = all_ports, duration = duration)
@@ -2612,7 +2881,7 @@ class STLClient(object):
while set(self.get_active_ports()).intersection(ports):
# make sure ASYNC thread is still alive - otherwise we will be stuck forever
- if not self.async_client.is_thread_alive():
+ if not self.async_client.is_active():
raise STLError("subscriber thread is dead")
time.sleep(0.01)
@@ -2626,16 +2895,22 @@ class STLClient(object):
@__api_check(True)
- def set_port_attr (self, ports = None, promiscuous = None, link_up = None, led_on = None, flow_ctrl = None):
+ def set_port_attr (self,
+ ports = None,
+ promiscuous = None,
+ link_up = None,
+ led_on = None,
+ flow_ctrl = None,
+ resolve = True):
"""
Set port attributes
:parameters:
- promiscuous - True or False
- link_up - True or False
- led_on - True or False
- flow_ctrl - 0: disable all, 1: enable tx side, 2: enable rx side, 3: full enable
-
+ promiscuous - True or False
+ link_up - True or False
+ led_on - True or False
+ flow_ctrl - 0: disable all, 1: enable tx side, 2: enable rx side, 3: full enable
+ resolve - if true, in case a destination address is configured as IPv4 try to resolve it
:raises:
+ :exe:'STLError'
@@ -2649,29 +2924,221 @@ class STLClient(object):
validate_type('link_up', link_up, (bool, type(None)))
validate_type('led_on', led_on, (bool, type(None)))
validate_type('flow_ctrl', flow_ctrl, (int, type(None)))
+
+ # common attributes for all ports
+ cmn_attr_dict = {}
- # build attributes
- attr_dict = {}
- if promiscuous is not None:
- attr_dict['promiscuous'] = {'enabled': promiscuous}
- if link_up is not None:
- attr_dict['link_status'] = {'up': link_up}
- if led_on is not None:
- attr_dict['led_status'] = {'on': led_on}
- if flow_ctrl is not None:
- attr_dict['flow_ctrl_mode'] = {'mode': flow_ctrl}
+ cmn_attr_dict['promiscuous'] = promiscuous
+ cmn_attr_dict['link_status'] = link_up
+ cmn_attr_dict['led_status'] = led_on
+ cmn_attr_dict['flow_ctrl_mode'] = flow_ctrl
- # no attributes to set
- if not attr_dict:
- return
-
+ # each port starts with a set of the common attributes
+ attr_dict = [dict(cmn_attr_dict) for _ in ports]
+
self.logger.pre_cmd("Applying attributes on port(s) {0}:".format(ports))
rc = self.__set_port_attr(ports, attr_dict)
self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+
+
+ @__api_check(True)
+ def set_service_mode (self, ports = None, enabled = True):
+ """
+ Set service mode for port(s)
+ In service mode ports will respond to ARP, PING and etc.
+
+ :parameters:
+ ports - for which ports to configure service mode on/off
+ enabled - True for activating service mode, False for disabling
+ :raises:
+ + :exe:'STLError'
+
+ """
+ # by default take all acquired ports
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ if enabled:
+ self.logger.pre_cmd('Enabling service mode on port(s) {0}:'.format(ports))
+ else:
+ self.logger.pre_cmd('Disabling service mode on port(s) {0}:'.format(ports))
+
+ rc = self.__set_service_mode(ports, enabled)
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+ @__api_check(True)
+ def resolve (self, ports = None, retries = 0, verbose = True):
+ """
+ Resolves ports (ARP resolution)
+
+ :parameters:
+ ports - for which ports to apply a unique sniffer (each port gets a unique file)
+ retires - how many times to retry on each port (intervals of 100 milliseconds)
+ verbose - log for each request the response
+ :raises:
+ + :exe:'STLError'
+
+ """
+ # by default - resolve all the ports that are configured with IPv4 dest
+ ports = ports if ports is not None else self.get_resolvable_ports()
+ ports = self._validate_port_list(ports)
+
+ self.logger.pre_cmd('Resolving destination on port(s) {0}:'.format(ports))
+ with self.logger.supress():
+ rc = self.__resolve(ports, retries)
+
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+ # print the ARP transaction
+ if verbose:
+ self.logger.log(rc)
+ self.logger.log('')
+
+
+
+ @__api_check(True)
+ def set_rx_sniffer (self, ports = None, base_filename = 'rx.pcap', limit = 1000):
+ """
+ Sets a RX sniffer for port(s) written to a PCAP file
+
+ :parameters:
+ ports - for which ports to apply a unique sniffer (each port gets a unique file)
+ base_filename - filename will be appended with '-<port_number>', e.g. rx.pcap --> rx-0.pcap, rx-1.pcap etc.
+ limit - limit how many packets will be written
+ :raises:
+ + :exe:'STLError'
+
+ """
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ # check arguments
+ validate_type('base_filename', base_filename, basestring)
+ validate_type('limit', limit, (int))
+ if limit <= 0:
+ raise STLError("'limit' must be a positive value")
+
+ self.logger.pre_cmd("Setting RX sniffers on port(s) {0}:".format(ports))
+ rc = self.__set_rx_sniffer(ports, base_filename, limit)
+ self.logger.post_cmd(rc)
+
if not rc:
raise STLError(rc)
+
+
+ @__api_check(True)
+ def remove_rx_sniffer (self, ports = None):
+ """
+ Removes RX sniffer from port(s)
+
+ :raises:
+ + :exe:'STLError'
+
+ """
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ self.logger.pre_cmd("Removing RX sniffers on port(s) {0}:".format(ports))
+ rc = self.__remove_rx_sniffer(ports)
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+ @__api_check(True)
+ def set_rx_queue (self, ports = None, size = 1000):
+ """
+ Sets RX queue for port(s)
+ The queue is cyclic and will hold last 'size' packets
+
+ :parameters:
+ ports - for which ports to apply a queue
+ size - size of the queue
+ :raises:
+ + :exe:'STLError'
+
+ """
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ # check arguments
+ validate_type('size', size, (int))
+ if size <= 0:
+ raise STLError("'size' must be a positive value")
+
+ self.logger.pre_cmd("Setting RX queue on port(s) {0}:".format(ports))
+ rc = self.__set_rx_queue(ports, size)
+ self.logger.post_cmd(rc)
+
+
+ if not rc:
+ raise STLError(rc)
+
+
+
+ @__api_check(True)
+ def remove_rx_queue (self, ports = None):
+ """
+ Removes RX queue from port(s)
+
+ :parameters:
+ ports - for which ports to remove the RX queue
+ :raises:
+ + :exe:'STLError'
+
+ """
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ self.logger.pre_cmd("Removing RX queue on port(s) {0}:".format(ports))
+ rc = self.__remove_rx_queue(ports)
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+
+ @__api_check(True)
+ def get_rx_queue_pkts (self, ports = None):
+ """
+ Returns any packets queued on the RX side by the server
+ return value is a dictonary per port
+
+ :parameters:
+ ports - for which ports to fetch
+ """
+
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ rc = self.__get_rx_queue_pkts(ports)
+ if not rc:
+ raise STLError(rc)
+
+ # decode the data back to the user
+ result = {}
+ for port, r in zip(ports, rc.data()):
+ result[port] = r
+
+ return result
+
+
def clear_events (self):
"""
Clear all events
@@ -2701,7 +3168,7 @@ class STLClient(object):
try:
rc = f(*args)
except STLError as e:
- client.logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold'))
+ client.logger.log("\nAction has failed with the following error:\n" + format_text(e.brief() + "\n", 'bold'))
return RC_ERR(e.brief())
# if got true - print time
@@ -2715,10 +3182,30 @@ class STLClient(object):
@__console
def ping_line (self, line):
- '''pings the server'''
- self.ping()
- return RC_OK()
+ '''pings the server / specific IP'''
+
+ # no parameters - so ping server
+ if not line:
+ self.ping_rpc_server()
+ return True
+
+ parser = parsing_opts.gen_parser(self,
+ "ping",
+ self.ping_line.__doc__,
+ parsing_opts.SINGLE_PORT,
+ parsing_opts.PING_IPV4,
+ parsing_opts.PKT_SIZE,
+ parsing_opts.PING_COUNT)
+ opts = parser.parse_args(line.split())
+ if not opts:
+ return opts
+
+ # IP ping
+ # source ports maps to ports as a single port
+ self.ping_ip(opts.ports[0], opts.ping_ipv4, opts.pkt_size, opts.count)
+
+
@__console
def shutdown_line (self, line):
'''shutdown the server'''
@@ -2846,13 +3333,14 @@ class STLClient(object):
parser = parsing_opts.gen_parser(self,
"reset",
self.reset_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL)
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.PORT_RESTART)
opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
if not opts:
return opts
- self.reset(ports = opts.ports)
+ self.reset(ports = opts.ports, restart = opts.restart)
return RC_OK()
@@ -2888,14 +3376,19 @@ class STLClient(object):
# just for sanity - will be checked on the API as well
self.__decode_core_mask(opts.ports, core_mask)
+ # for better use experience - check this first
+ try:
+ self.__pre_start_check(opts.ports, opts.force)
+ except STLError as e:
+ msg = e.brief()
+ self.logger.log(format_text(msg, 'bold'))
+ return RC_ERR(msg)
+
+
+ # stop ports if needed
active_ports = list_intersect(self.get_active_ports(), opts.ports)
- if active_ports:
- if not opts.force:
- msg = "Port(s) {0} are active - please stop them or add '--force'\n".format(active_ports)
- self.logger.log(format_text(msg, 'bold'))
- return RC_ERR(msg)
- else:
- self.stop(active_ports)
+ if active_ports and opts.force:
+ self.stop(active_ports)
# process tunables
@@ -2920,9 +3413,8 @@ class STLClient(object):
self.add_streams(profile.get_streams(), ports = port)
except STLError as e:
- error = 'Unknown error.'
- for line in e.brief().split('\n'):
- if line:
+ for line in e.brief().splitlines():
+ if ansi_len(line.strip()):
error = line
msg = format_text("\nError loading profile '{0}'".format(opts.file[0]), 'bold')
self.logger.log(msg + '\n')
@@ -3171,21 +3663,29 @@ class STLClient(object):
@__console
def push_line (self, line):
'''Push a pcap file '''
+ args = [self,
+ "push",
+ self.push_line.__doc__,
+ parsing_opts.REMOTE_FILE,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.COUNT,
+ parsing_opts.DURATION,
+ parsing_opts.IPG,
+ parsing_opts.MIN_IPG,
+ parsing_opts.SPEEDUP,
+ parsing_opts.FORCE,
+ parsing_opts.DUAL]
+
+ parser = parsing_opts.gen_parser(*(args + [parsing_opts.FILE_PATH_NO_CHECK]))
+ opts = parser.parse_args(line.split(), verify_acquired = True)
- parser = parsing_opts.gen_parser(self,
- "push",
- self.push_line.__doc__,
- parsing_opts.FILE_PATH,
- parsing_opts.REMOTE_FILE,
- parsing_opts.PORT_LIST_WITH_ALL,
- parsing_opts.COUNT,
- parsing_opts.DURATION,
- parsing_opts.IPG,
- parsing_opts.SPEEDUP,
- parsing_opts.FORCE,
- parsing_opts.DUAL)
+ if not opts:
+ return opts
+
+ if not opts.remote:
+ parser = parsing_opts.gen_parser(*(args + [parsing_opts.FILE_PATH]))
+ opts = parser.parse_args(line.split(), verify_acquired = True)
- opts = parser.parse_args(line.split(), verify_acquired = True)
if not opts:
return opts
@@ -3202,22 +3702,24 @@ class STLClient(object):
if opts.remote:
self.push_remote(opts.file[0],
- ports = opts.ports,
- ipg_usec = opts.ipg_usec,
- speedup = opts.speedup,
- count = opts.count,
- duration = opts.duration,
- is_dual = opts.dual)
+ ports = opts.ports,
+ ipg_usec = opts.ipg_usec,
+ min_ipg_usec = opts.min_ipg_usec,
+ speedup = opts.speedup,
+ count = opts.count,
+ duration = opts.duration,
+ is_dual = opts.dual)
else:
self.push_pcap(opts.file[0],
- ports = opts.ports,
- ipg_usec = opts.ipg_usec,
- speedup = opts.speedup,
- count = opts.count,
- duration = opts.duration,
- force = opts.force,
- is_dual = opts.dual)
+ ports = opts.ports,
+ ipg_usec = opts.ipg_usec,
+ min_ipg_usec = opts.min_ipg_usec,
+ speedup = opts.speedup,
+ count = opts.count,
+ duration = opts.duration,
+ force = opts.force,
+ is_dual = opts.dual)
@@ -3230,7 +3732,7 @@ class STLClient(object):
'''Sets port attributes '''
parser = parsing_opts.gen_parser(self,
- "port_attr",
+ "portattr",
self.set_port_attr_line.__doc__,
parsing_opts.PORT_LIST_WITH_ALL,
parsing_opts.PROMISCUOUS,
@@ -3244,18 +3746,18 @@ class STLClient(object):
if not opts:
return opts
- opts.prom = parsing_opts.ON_OFF_DICT.get(opts.prom)
- opts.link = parsing_opts.UP_DOWN_DICT.get(opts.link)
- opts.led = parsing_opts.ON_OFF_DICT.get(opts.led)
- opts.flow_ctrl = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl)
+ opts.prom = parsing_opts.ON_OFF_DICT.get(opts.prom)
+ opts.link = parsing_opts.UP_DOWN_DICT.get(opts.link)
+ opts.led = parsing_opts.ON_OFF_DICT.get(opts.led)
+ opts.flow_ctrl = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl)
# if no attributes - fall back to printing the status
- if not filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp]):
+ if not list(filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp])):
self.show_stats_line("--ps --port {0}".format(' '.join(str(port) for port in opts.ports)))
return
if opts.supp:
- info = self.ports[0].get_info() # assume for now all ports are same
+ info = self.ports[0].get_formatted_info() # assume for now all ports are same
print('')
print('Supported attributes for current NICs:')
print(' Promiscuous: yes')
@@ -3264,15 +3766,115 @@ class STLClient(object):
print(' Flow control: %s' % info['fc_supported'])
print('')
else:
- return self.set_port_attr(opts.ports, opts.prom, opts.link, opts.led, opts.flow_ctrl)
+ self.set_port_attr(opts.ports,
+ opts.prom,
+ opts.link,
+ opts.led,
+ opts.flow_ctrl)
+
+
+
+
+ @__console
+ def set_rx_sniffer_line (self, line):
+ '''Sets a port sniffer on RX channel in form of a PCAP file'''
+
+ parser = parsing_opts.gen_parser(self,
+ "set_rx_sniffer",
+ self.set_rx_sniffer_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.OUTPUT_FILENAME,
+ parsing_opts.LIMIT)
+
+ opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
+ if not opts:
+ return opts
+
+ self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit)
+
+ return RC_OK()
+
+
+ @__console
+ def resolve_line (self, line):
+ '''Performs a port ARP resolution'''
+
+ parser = parsing_opts.gen_parser(self,
+ "resolve",
+ self.resolve_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.RETRIES)
+
+ opts = parser.parse_args(line.split(), default_ports = self.get_resolvable_ports(), verify_acquired = True)
+ if not opts:
+ return opts
+
+ ports = list_intersect(opts.ports, self.get_resolvable_ports())
+ if not ports:
+ if not opts.ports:
+ msg = 'resolve - no ports with IPv4 destination'
+ else:
+ msg = 'pause - none of ports {0} are configured with IPv4 destination'.format(opts.ports)
+
+ self.logger.log(msg)
+ return RC_ERR(msg)
+
+ self.resolve(ports = ports, retries = opts.retries)
+
+ return RC_OK()
+
+
+ @__console
+ def set_l2_mode_line (self, line):
+ '''Configures a port in L2 mode'''
+ parser = parsing_opts.gen_parser(self,
+ "port",
+ self.set_l2_mode_line.__doc__,
+ parsing_opts.SINGLE_PORT,
+ parsing_opts.DST_MAC,
+ )
+ opts = parser.parse_args(line.split())
+ if not opts:
+ return opts
+
+
+ # source ports maps to ports as a single port
+ self.set_l2_mode(opts.ports[0], dst_mac = opts.dst_mac)
+
+ return RC_OK()
+
+
+ @__console
+ def set_l3_mode_line (self, line):
+ '''Configures a port in L3 mode'''
+
+ parser = parsing_opts.gen_parser(self,
+ "port",
+ self.set_l3_mode_line.__doc__,
+ parsing_opts.SINGLE_PORT,
+ parsing_opts.SRC_IPV4,
+ parsing_opts.DST_IPV4,
+ )
+
+ opts = parser.parse_args(line.split())
+ if not opts:
+ return opts
+
+
+ # source ports maps to ports as a single port
+ self.set_l3_mode(opts.ports[0], src_ipv4 = opts.src_ipv4, dst_ipv4 = opts.dst_ipv4)
+
+ return RC_OK()
+
+
@__console
def show_profile_line (self, line):
'''Shows profile information'''
parser = parsing_opts.gen_parser(self,
- "port",
+ "profile",
self.show_profile_line.__doc__,
parsing_opts.FILE_PATH)
@@ -3364,7 +3966,38 @@ class STLClient(object):
return "{0}(read-only)>".format(prefix)
elif self.is_all_ports_acquired():
- return "{0}>".format(prefix)
+
+ p = prefix
+
+ if self.get_service_enabled_ports():
+ if self.get_service_enabled_ports() == self.get_acquired_ports():
+ p += '(service)'
+ else:
+ p += '(service: {0})'.format(', '.join(map(str, self.get_service_enabled_ports())))
+
+ return "{0}>".format(p)
else:
- return "{0} {1}>".format(prefix, self.get_acquired_ports())
+ return "{0} (ports: {1})>".format(prefix, ', '.join(map(str, self.get_acquired_ports())))
+
+
+
+ @__console
+ def service_line (self, line):
+ '''Configures port for service mode.
+ In service mode ports will reply to ARP, PING
+ and etc.
+ '''
+
+ parser = parsing_opts.gen_parser(self,
+ "service",
+ self.service_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.SERVICE_OFF)
+
+ opts = parser.parse_args(line.split())
+ if not opts:
+ return opts
+
+ self.set_service_mode(ports = opts.ports, enabled = opts.enabled)
+
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
index 1461fcec..72c9317a 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
@@ -32,13 +32,29 @@ class BatchMessage(object):
id, msg = self.rpc_client.create_jsonrpc_v2(method_name, params, api_class, encode = False)
self.batch_list.append(msg)
- def invoke(self, block = False):
+ def invoke(self, block = False, chunk_size = 500000):
if not self.rpc_client.connected:
return RC_ERR("Not connected to server")
- msg = json.dumps(self.batch_list)
-
- return self.rpc_client.send_msg(msg)
+ if chunk_size:
+ response_batch = RC()
+ size = 0
+ new_batch = []
+ for msg in self.batch_list:
+ size += len(json.dumps(msg))
+ new_batch.append(msg)
+ if size > chunk_size:
+ batch_json = json.dumps(new_batch)
+ response_batch.add(self.rpc_client.send_msg(batch_json))
+ size = 0
+ new_batch = []
+ if new_batch:
+ batch_json = json.dumps(new_batch)
+ response_batch.add(self.rpc_client.send_msg(batch_json))
+ return response_batch
+ else:
+ batch_json = json.dumps(self.batch_list)
+ return self.rpc_client.send_msg(batch_json)
# JSON RPC v2.0 client
@@ -130,13 +146,13 @@ class JsonRpcClient(object):
if self.zipper.check_threshold(buffer):
response = self.send_raw_msg(self.zipper.compress(buffer))
- if response:
- response = self.zipper.decompress(response)
else:
response = self.send_raw_msg(buffer)
if not response:
return response
+ elif self.zipper.is_compressed(response):
+ response = self.zipper.decompress(response)
# return to string
response = response.decode()
@@ -172,6 +188,10 @@ class JsonRpcClient(object):
self.disconnect()
return RC_ERR("*** [RPC] - Failed to send message to server")
+ except KeyboardInterrupt as e:
+ # must restore the socket to a sane state
+ self.reconnect()
+ raise e
tries = 0
while True:
@@ -184,6 +204,10 @@ class JsonRpcClient(object):
self.disconnect()
return RC_ERR("*** [RPC] - Failed to get server response from {0}".format(self.transport))
+ except KeyboardInterrupt as e:
+ # must restore the socket to a sane state
+ self.reconnect()
+ raise e
return response
@@ -255,11 +279,6 @@ class JsonRpcClient(object):
self.connected = True
- rc = self.invoke_rpc_method('ping', api_class = None)
- if not rc:
- self.connected = False
- return rc
-
return RC_OK()
@@ -267,12 +286,6 @@ class JsonRpcClient(object):
# connect using current values
return self.connect()
- if not self.connected:
- return RC_ERR("Not connected to server")
-
- # reconnect
- return self.connect(self.server, self.port)
-
def is_connected(self):
return self.connected
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py
index dc06f9fb..6431b74a 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py
@@ -486,7 +486,7 @@ class CTRexScapyPktUtl(object):
if f.name == field_name:
return (l_offset+f.offset,f.get_size_bytes ());
- raise CTRexPacketBuildException(-11, "No layer %s-%d." % (name, save_cnt, field_name));
+ raise CTRexPacketBuildException(-11, "No layer %s-%d." % (field_name, layer_cnt))
def get_layer_offet_by_str(self, layer_des):
"""
@@ -827,6 +827,7 @@ class STLVmFixChecksumHw(CTRexVmDescBase):
self.l3_offset = l3_offset; # could be a name of offset
self.l4_offset = l4_offset; # could be a name of offset
self.l4_type = l4_type
+ self.l2_len = 0
def get_obj (self):
@@ -838,8 +839,8 @@ class STLVmFixChecksumHw(CTRexVmDescBase):
if type(self.l4_offset)==str:
self.l4_offset = parent._pkt_layer_offset(self.l4_offset);
- assert self.l4_offset >= self.l2_len+8, 'l4_offset should be higher than l3_offset offset'
- self.l3_len = self.l4_offset - self.l2_len;
+ assert self.l4_offset >= self.l2_len+8, 'l4_offset should be higher than l3_offset offset'
+ self.l3_len = self.l4_offset - self.l2_len;
class STLVmFixIpv4(CTRexVmDescBase):
@@ -1084,58 +1085,58 @@ class STLVmWrMaskFlowVar(CTRexVmDescBase):
class STLVmTrimPktSize(CTRexVmDescBase):
- """
- Trim the packet size by the stream variable size. This instruction only changes the total packet size, and does not repair the fields to match the new size.
+ def __init__(self,fv_name):
+ """
+ Trim the packet size by the stream variable size. This instruction only changes the total packet size, and does not repair the fields to match the new size.
- :parameters:
- fv_name : string
- Stream variable name. The value of this variable is the new total packet size.
+ :parameters:
+ fv_name : string
+ Stream variable name. The value of this variable is the new total packet size.
- For Example::
+ For Example::
- def create_stream (self):
- # pkt
- p_l2 = Ether();
- p_l3 = IP(src="16.0.0.1",dst="48.0.0.1")
- p_l4 = UDP(dport=12,sport=1025)
- pyld_size = max(0, self.max_pkt_size_l3 - len(p_l3/p_l4));
- base_pkt = p_l2/p_l3/p_l4/('\x55'*(pyld_size))
-
- l3_len_fix =-(len(p_l2));
- l4_len_fix =-(len(p_l2/p_l3));
-
-
- # vm
- vm = STLScVmRaw( [ STLVmFlowVar(name="fv_rand", min_value=64,
- max_value=len(base_pkt),
- size=2, op="inc"),
+ def create_stream (self):
+ # pkt
+ p_l2 = Ether();
+ p_l3 = IP(src="16.0.0.1",dst="48.0.0.1")
+ p_l4 = UDP(dport=12,sport=1025)
+ pyld_size = max(0, self.max_pkt_size_l3 - len(p_l3/p_l4));
+ base_pkt = p_l2/p_l3/p_l4/('\x55'*(pyld_size))
- STLVmTrimPktSize("fv_rand"), # change total packet size <<<
+ l3_len_fix =-(len(p_l2));
+ l4_len_fix =-(len(p_l2/p_l3));
- STLVmWrFlowVar(fv_name="fv_rand",
- pkt_offset= "IP.len",
- add_val=l3_len_fix), # fix ip len
- STLVmFixIpv4(offset = "IP"), # fix checksum
+ # vm
+ vm = STLScVmRaw( [ STLVmFlowVar(name="fv_rand", min_value=64,
+ max_value=len(base_pkt),
+ size=2, op="inc"),
- STLVmWrFlowVar(fv_name="fv_rand",
- pkt_offset= "UDP.len",
- add_val=l4_len_fix) # fix udp len
- ]
- )
-
- pkt = STLPktBuilder(pkt = base_pkt,
- vm = vm)
-
- return STLStream(packet = pkt,
- mode = STLTXCont())
+ STLVmTrimPktSize("fv_rand"), # change total packet size <<<
+ STLVmWrFlowVar(fv_name="fv_rand",
+ pkt_offset= "IP.len",
+ add_val=l3_len_fix), # fix ip len
- """
+ STLVmFixIpv4(offset = "IP"), # fix checksum
+
+ STLVmWrFlowVar(fv_name="fv_rand",
+ pkt_offset= "UDP.len",
+ add_val=l4_len_fix) # fix udp len
+ ]
+ )
+
+ pkt = STLPktBuilder(pkt = base_pkt,
+ vm = vm)
+
+ return STLStream(packet = pkt,
+ mode = STLTXCont())
+
+
+ """
- def __init__(self,fv_name):
super(STLVmTrimPktSize, self).__init__()
self.name = fv_name
validate_type('fv_name', fv_name, str)
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
index cec3761f..9eefc177 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
@@ -4,12 +4,14 @@ from collections import namedtuple, OrderedDict
from .trex_stl_packet_builder_scapy import STLPktBuilder
from .trex_stl_streams import STLStream
from .trex_stl_types import *
+from .trex_stl_rx_features import ARPResolver, PingResolver
from . import trex_stl_stats
from .utils.constants import FLOW_CTRL_DICT_REVERSED
import base64
import copy
from datetime import datetime, timedelta
+import threading
StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata'])
@@ -50,7 +52,9 @@ class Port(object):
def __init__ (self, port_id, user, comm_link, session_id, info):
self.port_id = port_id
+
self.state = self.STATE_IDLE
+
self.handler = None
self.comm_link = comm_link
self.transmit = comm_link.transmit
@@ -62,7 +66,7 @@ class Port(object):
self.streams = {}
self.profile = None
self.session_id = session_id
- self.attr = {}
+ self.status = {}
self.port_stats = trex_stl_stats.CPortStats(self)
@@ -72,31 +76,31 @@ class Port(object):
self.owner = ''
self.last_factor_type = None
-
+
+ self.__attr = {}
+ self.attr_lock = threading.Lock()
+
# decorator to verify port is up
def up(func):
- def func_wrapper(*args):
+ def func_wrapper(*args, **kwargs):
port = args[0]
if not port.is_up():
return port.err("{0} - port is down".format(func.__name__))
- return func(*args)
+ return func(*args, **kwargs)
return func_wrapper
# owned
def owned(func):
- def func_wrapper(*args):
+ def func_wrapper(*args, **kwargs):
port = args[0]
- if not port.is_up():
- return port.err("{0} - port is down".format(func.__name__))
-
if not port.is_acquired():
return port.err("{0} - port is not owned".format(func.__name__))
- return func(*args)
+ return func(*args, **kwargs)
return func_wrapper
@@ -106,14 +110,11 @@ class Port(object):
def func_wrapper(*args, **kwargs):
port = args[0]
- if not port.is_up():
- return port.err("{0} - port is down".format(func.__name__))
-
if not port.is_acquired():
return port.err("{0} - port is not owned".format(func.__name__))
if not port.is_writeable():
- return port.err("{0} - port is not in a writeable state".format(func.__name__))
+ return port.err("{0} - port is active, please stop the port before executing command".format(func.__name__))
return func(*args, **kwargs)
@@ -122,22 +123,22 @@ class Port(object):
def err(self, msg):
- return RC_ERR("port {0} : {1}\n".format(self.port_id, msg))
+ return RC_ERR("port {0} : *** {1}".format(self.port_id, msg))
def ok(self, data = ""):
return RC_OK(data)
def get_speed_bps (self):
- return (self.info['speed'] * 1000 * 1000 * 1000)
+ return (self.get_speed_gbps() * 1000 * 1000 * 1000)
- def get_formatted_speed (self):
- return "{0} Gbps".format(self.info['speed'])
+ def get_speed_gbps (self):
+ return self.__attr['speed']
def is_acquired(self):
return (self.handler != None)
def is_up (self):
- return (self.state != self.STATE_DOWN)
+ return self.__attr['link']['up']
def is_active(self):
return (self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) or (self.state == self.STATE_PCAP_TX)
@@ -165,7 +166,6 @@ class Port(object):
# take the port
- @up
def acquire(self, force = False, sync_streams = True):
params = {"port_id": self.port_id,
"user": self.user,
@@ -185,7 +185,6 @@ class Port(object):
# sync all the streams with the server
- @up
def sync_streams (self):
params = {"port_id": self.port_id}
@@ -201,7 +200,6 @@ class Port(object):
return self.ok()
# release the port
- @up
def release(self):
params = {"port_id": self.port_id,
"handler": self.handler}
@@ -219,7 +217,6 @@ class Port(object):
- @up
def sync(self):
params = {"port_id": self.port_id}
@@ -250,10 +247,10 @@ class Port(object):
self.next_available_id = int(rc.data()['max_stream_id']) + 1
- # attributes
- self.attr = rc.data()['attr']
- if 'speed' in rc.data():
- self.info['speed'] = rc.data()['speed'] // 1000
+ self.status = rc.data()
+
+ # replace the attributes in a thread safe manner
+ self.set_ts_attr(rc.data()['attr'])
return self.ok()
@@ -424,8 +421,8 @@ class Port(object):
# save this for TUI
self.last_factor_type = mul['type']
-
- return self.ok()
+
+ return rc
# stop traffic
@@ -445,8 +442,9 @@ class Port(object):
return self.err(rc.err())
self.state = self.STATE_STREAMS
+
self.last_factor_type = None
-
+
# timestamp for last tx
self.tx_stopped_ts = datetime.now()
@@ -487,6 +485,122 @@ class Port(object):
return self.ok()
+
+ @owned
+ def set_rx_sniffer (self, pcap_filename, limit):
+
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for performing RX capturing. Please enable service mode')
+
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "type": "capture",
+ "enabled": True,
+ "pcap_filename": pcap_filename,
+ "limit": limit}
+
+ rc = self.transmit("set_rx_feature", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.ok()
+
+
+ @owned
+ def remove_rx_sniffer (self):
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "type": "capture",
+ "enabled": False}
+
+ rc = self.transmit("set_rx_feature", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.ok()
+
+ @writeable
+ def set_l2_mode (self, dst_mac):
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for configuring L2 mode. Please enable service mode')
+
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "dst_mac": dst_mac}
+
+ rc = self.transmit("set_l2", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.sync()
+
+
+ @writeable
+ def set_l3_mode (self, src_addr, dest_addr, resolved_mac = None):
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for configuring L3 mode. Please enable service mode')
+
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "src_addr": src_addr,
+ "dst_addr": dest_addr}
+
+ if resolved_mac:
+ params["resolved_mac"] = resolved_mac
+
+ rc = self.transmit("set_l3", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.sync()
+
+
+ @owned
+ def set_rx_queue (self, size):
+
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "type": "queue",
+ "enabled": True,
+ "size": size}
+
+ rc = self.transmit("set_rx_feature", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.ok()
+
+ @owned
+ def remove_rx_queue (self):
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "type": "queue",
+ "enabled": False}
+
+ rc = self.transmit("set_rx_feature", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.ok()
+
+ @owned
+ def get_rx_queue_pkts (self):
+ params = {"handler": self.handler,
+ "port_id": self.port_id}
+
+ rc = self.transmit("get_rx_queue_pkts", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ pkts = rc.data()['pkts']
+
+ # decode the packets from base64 to binary
+ for i in range(len(pkts)):
+ pkts[i]['binary'] = base64.b64decode(pkts[i]['binary'])
+
+ return RC_OK(pkts)
+
+
@owned
def pause (self):
@@ -568,23 +682,60 @@ class Port(object):
@owned
- def set_attr (self, attr_dict):
+ def set_attr (self, **kwargs):
+
+ json_attr = {}
+
+ if kwargs.get('promiscuous') is not None:
+ json_attr['promiscuous'] = {'enabled': kwargs.get('promiscuous')}
+
+ if kwargs.get('link_status') is not None:
+ json_attr['link_status'] = {'up': kwargs.get('link_status')}
+
+ if kwargs.get('led_status') is not None:
+ json_attr['led_status'] = {'on': kwargs.get('led_status')}
+
+ if kwargs.get('flow_ctrl_mode') is not None:
+ json_attr['flow_ctrl_mode'] = {'mode': kwargs.get('flow_ctrl_mode')}
+
+ if kwargs.get('rx_filter_mode') is not None:
+ json_attr['rx_filter_mode'] = {'mode': kwargs.get('rx_filter_mode')}
+
params = {"handler": self.handler,
"port_id": self.port_id,
- "attr": attr_dict}
+ "attr": json_attr}
rc = self.transmit("set_port_attr", params)
if rc.bad():
return self.err(rc.err())
+ # update the dictionary from the server explicitly
+ return self.sync()
- #self.attr.update(attr_dict)
-
+
+ @owned
+ def set_service_mode (self, enabled):
+ rc = self.set_attr(rx_filter_mode = 'all' if enabled else 'hw')
+ if not rc:
+ return rc
+
+ if not enabled:
+ rc = self.remove_rx_queue()
+ if not rc:
+ return rc
+
+ rc = self.remove_rx_sniffer()
+ if not rc:
+ return rc
+
return self.ok()
+ def is_service_mode_on (self):
+ return self.get_rx_filter_mode() == 'all'
+
@writeable
- def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler):
+ def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler, min_ipg_usec):
params = {"handler": self.handler,
"port_id": self.port_id,
@@ -594,7 +745,8 @@ class Port(object):
"count": count,
"duration": duration,
"is_dual": is_dual,
- "slave_handler": slave_handler}
+ "slave_handler": slave_handler,
+ "min_ipg_usec": min_ipg_usec if min_ipg_usec else 0}
rc = self.transmit("push_remote", params)
if rc.bad():
@@ -607,7 +759,16 @@ class Port(object):
def get_profile (self):
return self.profile
-
+ # invalidates the current ARP
+ def invalidate_arp (self):
+ dest = self.__attr['dest']
+
+ if dest['type'] != 'mac':
+ return self.set_attr(dest = dest['ipv4'])
+ else:
+ return self.ok()
+
+
def print_profile (self, mult, duration):
if not self.get_profile():
return
@@ -648,24 +809,32 @@ class Port(object):
format_time(exp_time_factor_sec)))
print("\n")
- # generate port info
- def get_info (self):
+ # generate formatted (console friendly) port info
+ def get_formatted_info (self, sync = True):
+
+ # sync the status
+ if sync:
+ self.sync()
+
+ # get a copy of the current attribute set (safe against manipulation)
+ attr = self.get_ts_attr()
+
info = dict(self.info)
info['status'] = self.get_port_state_name()
- if 'link' in self.attr:
- info['link'] = 'UP' if self.attr['link']['up'] else 'DOWN'
+ if 'link' in attr:
+ info['link'] = 'UP' if attr['link']['up'] else 'DOWN'
else:
info['link'] = 'N/A'
- if 'fc' in self.attr:
- info['fc'] = FLOW_CTRL_DICT_REVERSED.get(self.attr['fc']['mode'], 'N/A')
+ if 'fc' in attr:
+ info['fc'] = FLOW_CTRL_DICT_REVERSED.get(attr['fc']['mode'], 'N/A')
else:
info['fc'] = 'N/A'
- if 'promiscuous' in self.attr:
- info['prom'] = "on" if self.attr['promiscuous']['enabled'] else "off"
+ if 'promiscuous' in attr:
+ info['prom'] = "on" if attr['promiscuous']['enabled'] else "off"
else:
info['prom'] = "N/A"
@@ -692,34 +861,139 @@ class Port(object):
else:
info['is_virtual'] = 'N/A'
+ # speed
+ info['speed'] = self.get_speed_gbps()
+
+ # RX filter mode
+ info['rx_filter_mode'] = 'hardware match' if attr['rx_filter_mode'] == 'hw' else 'fetch all'
+
+ # src MAC and IPv4
+ info['src_mac'] = attr['src_mac']
+ info['src_ipv4'] = attr['src_ipv4']
+
+ if info['src_ipv4'] is None:
+ info['src_ipv4'] = '-'
+
+ # dest
+ dest = attr['dest']
+ if dest['type'] == 'mac':
+ info['dest'] = dest['mac']
+ info['arp'] = '-'
+
+ elif dest['type'] == 'ipv4':
+ info['dest'] = dest['ipv4']
+ info['arp'] = dest['arp']
+
+ elif dest['type'] == 'ipv4_u':
+ info['dest'] = dest['ipv4']
+ info['arp'] = 'unresolved'
+
+
+ # RX info
+ rx_info = self.status['rx_info']
+
+ # RX sniffer
+ sniffer = rx_info['sniffer']
+ info['rx_sniffer'] = '{0}\n[{1} / {2}]'.format(sniffer['pcap_filename'], sniffer['count'], sniffer['limit']) if sniffer['is_active'] else 'off'
+
+
+ # RX queue
+ queue = rx_info['queue']
+ info['rx_queue'] = '[{0} / {1}]'.format(queue['count'], queue['size']) if queue['is_active'] else 'off'
+
+ # Grat ARP
+ grat_arp = rx_info['grat_arp']
+ if grat_arp['is_active']:
+ info['grat_arp'] = "every {0} seconds".format(grat_arp['interval_sec'])
+ else:
+ info['grat_arp'] = "off"
+
+
return info
def get_port_state_name(self):
return self.STATES_MAP.get(self.state, "Unknown")
+ def get_src_addr (self):
+ src_mac = self.__attr['src_mac']
+ src_ipv4 = self.__attr['src_ipv4']
+
+ return {'mac': src_mac, 'ipv4': src_ipv4}
+
+ def get_rx_filter_mode (self):
+ return self.__attr['rx_filter_mode']
+
+ def get_dst_addr (self):
+ dest = self.__attr['dest']
+
+ if dest['type'] == 'mac':
+ return {'ipv4': None, 'mac': dest['mac']}
+
+ elif dest['type'] == 'ipv4':
+ return {'ipv4': dest['ipv4'], 'mac': dest['arp']}
+
+ elif dest['type'] == 'ipv4_u':
+ return {'ipv4': dest['ipv4'], 'mac': None}
+
+ else:
+ assert(0)
+
+
+ # port is considered resolved if it's dest is either MAC or resolved IPv4
+ def is_resolved (self):
+ return (self.get_dst_addr()['mac'] is not None)
+
+ # return True if the port is valid for resolve (has an IPv4 address as dest)
+ def is_resolvable (self):
+ return (self.get_dst_addr()['ipv4'] is not None)
+
+ @writeable
+ def arp_resolve (self, retries):
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for performing ARP resolution. Please enable service mode')
+
+ return ARPResolver(self).resolve(retries)
+
+ @writeable
+ def ping (self, ping_ipv4, pkt_size):
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for performing ping. Please enable service mode')
+
+ return PingResolver(self, ping_ipv4, pkt_size).resolve()
+
+
################# stats handler ######################
def generate_port_stats(self):
return self.port_stats.generate_stats()
def generate_port_status(self):
- info = self.get_info()
+ info = self.get_formatted_info()
- return {"driver": info['driver'],
- "description": info.get('description', 'N/A')[:18],
- "HW src mac": info['hw_macaddr'],
- "SW src mac": info['src_macaddr'],
- "SW dst mac": info['dst_macaddr'],
- "PCI Address": info['pci_addr'],
- "NUMA Node": info['numa'],
+ return {"driver": info['driver'],
+ "description": info.get('description', 'N/A')[:18],
+ "src MAC": info['src_mac'],
+ "src IPv4": info['src_ipv4'],
+ "Destination": info['dest'],
+ "ARP Resolution": format_text("{0}".format(info['arp']), 'bold', 'red') if info['arp'] == 'unresolved' else info['arp'],
+ "PCI Address": info['pci_addr'],
+ "NUMA Node": info['numa'],
"--": "",
"---": "",
- "link speed": "{speed} Gb/s".format(speed=info['speed']),
+ "----": "",
+ "-----": "",
+ "link speed": "%g Gb/s" % info['speed'],
"port status": info['status'],
"link status": info['link'],
"promiscuous" : info['prom'],
"flow ctrl" : info['fc'],
+
+ "RX Filter Mode": info['rx_filter_mode'],
+ "RX Queueing": info['rx_queue'],
+ "RX sniffer": info['rx_sniffer'],
+ "Grat ARP": info['grat_arp'],
+
}
def clear_stats(self):
@@ -756,17 +1030,54 @@ class Port(object):
return {"streams" : OrderedDict(sorted(data.items())) }
-
+ ######## attributes are a complex type (dict) that might be manipulated through the async thread #############
+
+ # get in a thread safe manner a duplication of attributes
+ def get_ts_attr (self):
+ with self.attr_lock:
+ return dict(self.__attr)
+
+ # set in a thread safe manner a new dict of attributes
+ def set_ts_attr (self, new_attr):
+ with self.attr_lock:
+ self.__attr = new_attr
+
+
################# events handler ######################
def async_event_port_job_done (self):
# until thread is locked - order is important
self.tx_stopped_ts = datetime.now()
self.state = self.STATE_STREAMS
+
self.last_factor_type = None
- def async_event_port_attr_changed (self, attr):
- self.info['speed'] = attr['speed'] // 1000
- self.attr = attr
+ def async_event_port_attr_changed (self, new_attr):
+
+ # get a thread safe duplicate
+ cur_attr = self.get_ts_attr()
+
+ # check if anything changed
+ if new_attr == cur_attr:
+ return None
+
+ # generate before
+ before = self.get_formatted_info(sync = False)
+
+ # update
+ self.set_ts_attr(new_attr)
+
+ # generate after
+ after = self.get_formatted_info(sync = False)
+
+ # return diff
+ diff = {}
+ for key, new_value in after.items():
+ old_value = before.get(key, 'N/A')
+ if new_value != old_value:
+ diff[key] = (old_value, new_value)
+
+ return diff
+
# rest of the events are used for TUI / read only sessions
def async_event_port_stopped (self):
@@ -792,3 +1103,4 @@ class Port(object):
def async_event_released (self):
self.owner = ''
+
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py
new file mode 100644
index 00000000..ec83de5d
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py
@@ -0,0 +1,255 @@
+
+from .trex_stl_streams import STLStream, STLTXSingleBurst
+from .trex_stl_packet_builder_scapy import STLPktBuilder
+
+from scapy.layers.l2 import Ether, ARP
+from scapy.layers.inet import IP, ICMP
+
+import time
+
+# a generic abstract class for resolving using the server
+class Resolver(object):
+ def __init__ (self, port, queue_size = 100):
+ self.port = port
+
+ # code to execute before sending any request - return RC object
+ def pre_send (self):
+ raise NotImplementedError()
+
+ # return a list of streams for request
+ def generate_request (self):
+ raise NotImplementedError()
+
+ # return None for more packets otherwise RC object
+ def on_pkt_rx (self, pkt):
+ raise NotImplementedError()
+
+ # return value in case of timeout
+ def on_timeout_err (self, retries):
+ raise NotImplementedError()
+
+ ##################### API ######################
+ def resolve (self, retries = 0):
+
+ # first cleanup
+ rc = self.port.remove_all_streams()
+ if not rc:
+ return rc
+
+ # call the specific class implementation
+ rc = self.pre_send()
+ if not rc:
+ return rc
+
+ # start the iteration
+ try:
+
+ # add the stream(s)
+ self.port.add_streams(self.generate_request())
+ rc = self.port.set_rx_queue(size = 100)
+ if not rc:
+ return rc
+
+ return self.resolve_wrapper(retries)
+
+ finally:
+ # best effort restore
+ self.port.remove_rx_queue()
+ self.port.remove_all_streams()
+
+
+ # main resolve function
+ def resolve_wrapper (self, retries):
+
+ # retry for 'retries'
+ index = 0
+ while True:
+ rc = self.resolve_iteration()
+ if rc is not None:
+ return rc
+
+ if index >= retries:
+ return self.on_timeout_err(retries)
+
+ index += 1
+ time.sleep(0.1)
+
+
+
+ def resolve_iteration (self):
+
+ mult = {'op': 'abs', 'type' : 'percentage', 'value': 100}
+ rc = self.port.start(mul = mult, force = False, duration = -1, mask = 0xffffffff)
+ if not rc:
+ return rc
+
+ # save the start timestamp
+ self.start_ts = rc.data()['ts']
+
+ # block until traffic finishes
+ while self.port.is_active():
+ time.sleep(0.01)
+
+ return self.wait_for_rx_response()
+
+
+ def wait_for_rx_response (self):
+
+ # we try to fetch response for 5 times
+ polling = 5
+
+ while polling > 0:
+
+ # fetch the queue
+ rx_pkts = self.port.get_rx_queue_pkts()
+
+ # might be an error
+ if not rx_pkts:
+ return rx_pkts
+
+ # for each packet - examine it
+ for pkt in rx_pkts.data():
+ rc = self.on_pkt_rx(pkt)
+ if rc is not None:
+ return rc
+
+ if polling == 0:
+ return None
+
+ polling -= 1
+ time.sleep(0.1)
+
+
+
+
+
+class ARPResolver(Resolver):
+ def __init__ (self, port_id):
+ super(ARPResolver, self).__init__(port_id)
+
+ # before resolve
+ def pre_send (self):
+ self.dst = self.port.get_dst_addr()
+ self.src = self.port.get_src_addr()
+
+ if self.dst['ipv4'] is None:
+ return self.port.err("Port has a non-IPv4 destination: '{0}'".format(self.dst['mac']))
+
+ if self.src['ipv4'] is None:
+ return self.port.err('Port must have an IPv4 source address configured')
+
+ # invalidate the current ARP resolution (if exists)
+ return self.port.invalidate_arp()
+
+
+ # return a list of streams for request
+ def generate_request (self):
+
+ base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = self.src['ipv4'], pdst = self.dst['ipv4'], hwsrc = self.src['mac'])
+ s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
+
+ return [s1]
+
+
+ # return None in case more packets are needed else the status rc
+ def on_pkt_rx (self, pkt):
+ scapy_pkt = Ether(pkt['binary'])
+ if not 'ARP' in scapy_pkt:
+ return None
+
+ arp = scapy_pkt['ARP']
+
+ # check this is the right ARP (ARP reply with the address)
+ if (arp.op != 2) or (arp.psrc != self.dst['ipv4']):
+ return None
+
+
+ # update the port with L3 full configuration
+ rc = self.port.set_l3_mode(self.src['ipv4'], self.dst['ipv4'], arp.hwsrc)
+ if not rc:
+ return rc
+
+ return self.port.ok('Port {0} - Recieved ARP reply from: {1}, hw: {2}'.format(self.port.port_id, arp.psrc, arp.hwsrc))
+
+
+ def on_timeout_err (self, retries):
+ return self.port.err('failed to receive ARP response ({0} retries)'.format(retries))
+
+
+
+
+ #################### ping resolver ####################
+
+class PingResolver(Resolver):
+ def __init__ (self, port, ping_ip, pkt_size):
+ super(PingResolver, self).__init__(port)
+ self.ping_ip = ping_ip
+ self.pkt_size = pkt_size
+
+ def pre_send (self):
+
+ self.src = self.port.get_src_addr()
+ self.dst = self.port.get_dst_addr()
+
+ if self.src['ipv4'] is None:
+ return self.port.err('Ping - port does not have an IPv4 address configured')
+
+ if self.dst['mac'] is None:
+ return self.port.err('Ping - port has an unresolved destination, cannot determine next hop MAC address')
+
+ return self.port.ok()
+
+
+ # return a list of streams for request
+ def generate_request (self):
+
+ base_pkt = Ether(dst = self.dst['mac'])/IP(src = self.src['ipv4'], dst = self.ping_ip)/ICMP(type = 8)
+ pad = max(0, self.pkt_size - len(base_pkt))
+
+ base_pkt = base_pkt / (pad * 'x')
+
+ #base_pkt.show2()
+ s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
+
+ self.base_pkt = base_pkt
+
+ return [s1]
+
+ # return None for more packets otherwise RC object
+ def on_pkt_rx (self, pkt):
+ scapy_pkt = Ether(pkt['binary'])
+ if not 'ICMP' in scapy_pkt:
+ return None
+
+ ip = scapy_pkt['IP']
+ if ip.dst != self.src['ipv4']:
+ return None
+
+ icmp = scapy_pkt['ICMP']
+
+ dt = pkt['ts'] - self.start_ts
+
+ # echo reply
+ if icmp.type == 0:
+ # check seq
+ if icmp.seq != self.base_pkt['ICMP'].seq:
+ return None
+ return self.port.ok('Reply from {0}: bytes={1}, time={2:.2f}ms, TTL={3}'.format(ip.src, len(pkt['binary']), dt * 1000, ip.ttl))
+
+ # unreachable
+ elif icmp.type == 3:
+ # check seq
+ if icmp.payload.seq != self.base_pkt['ICMP'].seq:
+ return None
+ return self.port.ok('Reply from {0}: Destination host unreachable'.format(icmp.src))
+
+ else:
+ # skip any other types
+ #scapy_pkt.show2()
+ return None
+
+
+
+ # return the str of a timeout err
+ def on_timeout_err (self, retries):
+ return self.port.ok('Request timed out.')
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
index 9f601484..c08a0af8 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
@@ -670,12 +670,19 @@ class CTRexInfoGenerator(object):
("promiscuous", []),
("flow ctrl", []),
("--", []),
- ("HW src mac", []),
- ("SW src mac", []),
- ("SW dst mac", []),
+ ("src IPv4", []),
+ ("src MAC", []),
("---", []),
+ ("Destination", []),
+ ("ARP Resolution", []),
+ ("----", []),
("PCI Address", []),
("NUMA Node", []),
+ ("-----", []),
+ ("RX Filter Mode", []),
+ ("RX Queueing", []),
+ ("RX sniffer", []),
+ ("Grat ARP", []),
]
)
@@ -1103,13 +1110,7 @@ class CPortStats(CTRexStats):
port_state = format_text(port_state, 'bold')
if self._port_obj:
- if 'link' in self._port_obj.attr:
- if self._port_obj.attr.get('link', {}).get('up') == False:
- link_state = format_text('DOWN', 'red', 'bold')
- else:
- link_state = 'UP'
- else:
- link_state = 'N/A'
+ link_state = 'UP' if self._port_obj.is_up() else format_text('DOWN', 'red', 'bold')
else:
link_state = ''
@@ -1130,7 +1131,7 @@ class CPortStats(CTRexStats):
return {"owner": owner,
"state": "{0}".format(port_state),
'link': link_state,
- "speed": self._port_obj.get_formatted_speed() if self._port_obj else '',
+ "speed": "%g Gb/s" % self._port_obj.get_speed_gbps() if self._port_obj else '',
"CPU util.": "{0} {1}%".format(self.get_trend_gui("m_cpu_util", use_raw = True),
format_threshold(round_float(self.get("m_cpu_util")), [85, 100], [0, 85])) if self._port_obj else '' ,
"--": " ",
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
index e63f9125..26613e56 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
@@ -987,7 +987,8 @@ class STLProfile(object):
loop_count = 1,
vm = None,
packet_hook = None,
- split_mode = None):
+ split_mode = None,
+ min_ipg_usec = None):
""" Convert a pcap file with a number of packets to a list of connected streams.
packet1->packet2->packet3 etc
@@ -1017,6 +1018,9 @@ class STLProfile(object):
used for dual mode
can be 'MAC' or 'IP'
+ min_ipg_usec : float
+ Minumum inter packet gap in usec. Used to guard from too small IPGs.
+
:return: STLProfile
"""
@@ -1024,9 +1028,15 @@ class STLProfile(object):
# check filename
if not os.path.isfile(pcap_file):
raise STLError("file '{0}' does not exists".format(pcap_file))
+ if speedup <= 0:
+ raise STLError('Speedup should not be negative.')
+ if min_ipg_usec and min_ipg_usec < 0:
+ raise STLError('min_ipg_usec should not be negative.')
+
- # make sure IPG is not less than 1 usec
- if ipg_usec is not None and ipg_usec < 0.001:
+ # make sure IPG is not less than 0.001 usec
+ if (ipg_usec is not None and (ipg_usec < 0.001 * speedup) and
+ (min_ipg_usec is None or min_ipg_usec < 0.001)):
raise STLError("ipg_usec cannot be less than 0.001 usec: '{0}'".format(ipg_usec))
if loop_count < 0:
@@ -1039,6 +1049,7 @@ class STLProfile(object):
pkts = PCAPReader(pcap_file).read_all()
return STLProfile.__pkts_to_streams(pkts,
ipg_usec,
+ min_ipg_usec,
speedup,
loop_count,
vm,
@@ -1046,8 +1057,20 @@ class STLProfile(object):
else:
pkts_a, pkts_b = PCAPReader(pcap_file).read_all(split_mode = split_mode)
+ # swap the packets if a is empty, or the ts of first packet in b is earlier
+ if not pkts_a:
+ pkts_a, pkts_b = pkts_b, pkts_a
+ elif (ipg_usec is None) and pkts_b:
+ meta = pkts_a[0][1]
+ start_time_a = meta[0] * 1e6 + meta[1]
+ meta = pkts_b[0][1]
+ start_time_b = meta[0] * 1e6 + meta[1]
+ if start_time_b < start_time_a:
+ pkts_a, pkts_b = pkts_b, pkts_a
+
profile_a = STLProfile.__pkts_to_streams(pkts_a,
ipg_usec,
+ min_ipg_usec,
speedup,
loop_count,
vm,
@@ -1056,6 +1079,7 @@ class STLProfile(object):
profile_b = STLProfile.__pkts_to_streams(pkts_b,
ipg_usec,
+ min_ipg_usec,
speedup,
loop_count,
vm,
@@ -1070,23 +1094,27 @@ class STLProfile(object):
@staticmethod
- def __pkts_to_streams (pkts, ipg_usec, speedup, loop_count, vm, packet_hook, start_delay_usec = 0):
+ def __pkts_to_streams (pkts, ipg_usec, min_ipg_usec, speedup, loop_count, vm, packet_hook, start_delay_usec = 0):
streams = []
-
- # 10 ms delay before starting the PCAP
- last_ts_usec = -(start_delay_usec)
-
if packet_hook:
pkts = [(packet_hook(cap), meta) for (cap, meta) in pkts]
-
for i, (cap, meta) in enumerate(pkts, start = 1):
# IPG - if not provided, take from cap
- if ipg_usec == None:
- ts_usec = (meta[0] * 1e6 + meta[1]) / float(speedup)
- else:
- ts_usec = (ipg_usec * i) / float(speedup)
+ if ipg_usec is None:
+ packet_time = meta[0] * 1e6 + meta[1]
+ if i == 1:
+ prev_time = packet_time
+ isg = (packet_time - prev_time) / float(speedup)
+ if min_ipg_usec and isg < min_ipg_usec:
+ isg = min_ipg_usec
+ prev_time = packet_time
+ else: # user specified ipg
+ if min_ipg_usec:
+ isg = min_ipg_usec
+ else:
+ isg = ipg_usec / float(speedup)
# handle last packet
if i == len(pkts):
@@ -1100,13 +1128,11 @@ class STLProfile(object):
packet = STLPktBuilder(pkt_buffer = cap, vm = vm),
mode = STLTXSingleBurst(total_pkts = 1, percentage = 100),
self_start = True if (i == 1) else False,
- isg = (ts_usec - last_ts_usec), # seconds to usec
+ isg = isg, # usec
action_count = action_count,
next = next))
-
- last_ts_usec = ts_usec
-
+
profile = STLProfile(streams)
profile.meta = {'type': 'pcap'}
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
index aa6c4218..a60a7ede 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
@@ -50,11 +50,25 @@ class RC():
return (e if len(e) != 1 else e[0])
def __str__ (self):
- s = ""
- for x in self.rc_list:
- if x.data:
- s += format_text("\n{0}".format(x.data), 'bold')
- return s
+ if self.good():
+ s = ""
+ for x in self.rc_list:
+ if x.data:
+ s += format_text("\n{0}".format(x.data), 'bold')
+ return s
+ else:
+ show_count = 10
+ err_list = []
+ err_count = 0
+ for x in filter(len, listify(self.err())):
+ err_count += 1
+ if len(err_list) < show_count:
+ err_list.append(format_text(x, 'bold'))
+ s = '\n' if len(err_list) > 1 else ''
+ if err_count > show_count:
+ s += format_text('Occurred %s errors, showing first %s:\n' % (err_count, show_count), 'bold')
+ s += '\n'.join(err_list)
+ return s
def __iter__(self):
return self.rc_list.__iter__()
@@ -135,6 +149,12 @@ def validate_type(arg_name, arg, valid_types):
else:
raise STLError('validate_type: valid_types should be type or list or tuple of types')
+
+def validate_choice (arg_name, arg, choices):
+ if arg is not None and not arg in choices:
+ raise STLError("validate_choice: argument '{0}' can only be one of '{1}'".format(arg_name, choices))
+
+
# throws STLError if not exactly one argument is present
def verify_exclusive_arg (args_list):
if not (len(list(filter(lambda x: x is not None, args_list))) == 1):
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
index 72ee8972..cbbacb27 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
@@ -3,6 +3,8 @@ import sys
import string
import random
import time
+import socket
+import re
try:
import pwd
@@ -86,3 +88,23 @@ class PassiveTimer(object):
return (time.time() > self.expr_sec)
+def is_valid_ipv4 (addr):
+ try:
+ socket.inet_pton(socket.AF_INET, addr)
+ return True
+ except (socket.error, TypeError):
+ return False
+
+def is_valid_mac (mac):
+ return bool(re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac.lower()))
+
+def list_remove_dup (l):
+ tmp = list()
+
+ for x in l:
+ if not x in tmp:
+ tmp.append(x)
+
+ return tmp
+
+
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
index 7eda8635..f5dab30c 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
@@ -1,6 +1,6 @@
import argparse
from collections import namedtuple, OrderedDict
-from .common import list_intersect, list_difference
+from .common import list_intersect, list_difference, is_valid_ipv4, is_valid_mac, list_remove_dup
from .text_opts import format_text
from ..trex_stl_types import *
from .constants import ON_OFF_DICT, UP_DOWN_DICT, FLOW_CTRL_DICT
@@ -14,56 +14,85 @@ ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options'])
# list of available parsing options
-MULTIPLIER = 1
-MULTIPLIER_STRICT = 2
-PORT_LIST = 3
-ALL_PORTS = 4
-PORT_LIST_WITH_ALL = 5
-FILE_PATH = 6
-FILE_FROM_DB = 7
-SERVER_IP = 8
-STREAM_FROM_PATH_OR_FILE = 9
-DURATION = 10
-FORCE = 11
-DRY_RUN = 12
-XTERM = 13
-TOTAL = 14
-FULL_OUTPUT = 15
-IPG = 16
-SPEEDUP = 17
-COUNT = 18
-PROMISCUOUS = 19
-LINK_STATUS = 20
-LED_STATUS = 21
-TUNABLES = 22
-REMOTE_FILE = 23
-LOCKED = 24
-PIN_CORES = 25
-CORE_MASK = 26
-DUAL = 27
-FLOW_CTRL = 28
-SUPPORTED = 29
-
-GLOBAL_STATS = 50
-PORT_STATS = 51
-PORT_STATUS = 52
-STREAMS_STATS = 53
-STATS_MASK = 54
-CPU_STATS = 55
-MBUF_STATS = 56
-EXTENDED_STATS = 57
-EXTENDED_INC_ZERO_STATS = 58
-
-STREAMS_MASK = 60
-CORE_MASK_GROUP = 61
-
-# ALL_STREAMS = 61
-# STREAM_LIST_WITH_ALL = 62
+_constants = '''
+
+MULTIPLIER
+MULTIPLIER_STRICT
+PORT_LIST
+ALL_PORTS
+PORT_LIST_WITH_ALL
+FILE_PATH
+FILE_FROM_DB
+SERVER_IP
+STREAM_FROM_PATH_OR_FILE
+DURATION
+FORCE
+DRY_RUN
+XTERM
+TOTAL
+FULL_OUTPUT
+IPG
+MIN_IPG
+SPEEDUP
+COUNT
+PROMISCUOUS
+LINK_STATUS
+LED_STATUS
+TUNABLES
+REMOTE_FILE
+LOCKED
+PIN_CORES
+CORE_MASK
+DUAL
+FLOW_CTRL
+SUPPORTED
+FILE_PATH_NO_CHECK
+
+OUTPUT_FILENAME
+LIMIT
+PORT_RESTART
+
+RETRIES
+
+SINGLE_PORT
+DST_MAC
+
+PING_IPV4
+PING_COUNT
+PKT_SIZE
+
+SERVICE_OFF
+
+SRC_IPV4
+DST_IPV4
+
+GLOBAL_STATS
+PORT_STATS
+PORT_STATUS
+STREAMS_STATS
+STATS_MASK
+CPU_STATS
+MBUF_STATS
+EXTENDED_STATS
+EXTENDED_INC_ZERO_STATS
+
+STREAMS_MASK
+CORE_MASK_GROUP
+
+# ALL_STREAMS
+# STREAM_LIST_WITH_ALL
+# list of ArgumentGroup types
+MUTEX
+'''
+
+for index, line in enumerate(_constants.splitlines()):
+ var = line.strip().split()
+ if not var or '#' in var[0]:
+ continue
+ exec('%s = %s' % (var[0], index))
-# list of ArgumentGroup types
-MUTEX = 1
def check_negative(value):
ivalue = int(value)
@@ -217,8 +246,30 @@ def is_valid_file(filename):
return filename
+def check_ipv4_addr (ipv4_str):
+ if not is_valid_ipv4(ipv4_str):
+ raise argparse.ArgumentTypeError("invalid IPv4 address: '{0}'".format(ipv4_str))
+ return ipv4_str
+
+def check_pkt_size (pkt_size):
+ try:
+ pkt_size = int(pkt_size)
+ except ValueError:
+ raise argparse.ArgumentTypeError("invalid packet size type: '{0}'".format(pkt_size))
+
+ if (pkt_size < 64) or (pkt_size > 9216):
+ raise argparse.ArgumentTypeError("invalid packet size: '{0}' - valid range is 64 to 9216".format(pkt_size))
+
+ return pkt_size
+
+def check_mac_addr (addr):
+ if not is_valid_mac(addr):
+ raise argparse.ArgumentTypeError("not a valid MAC address: '{0}'".format(addr))
+
+ return addr
+
def decode_tunables (tunable_str):
tunables = {}
@@ -273,6 +324,11 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'default': None,
'type': float}),
+ MIN_IPG: ArgumentPack(['--min-ipg'],
+ {'help': "Minimal IPG value in usec between packets. Used to guard from too small IPGs.",
+ 'dest': "min_ipg_usec",
+ 'default': None,
+ 'type': float}),
SPEEDUP: ArgumentPack(['-s', '--speedup'],
{'help': "Factor to accelerate the injection. effectively means IPG = IPG / SPEEDUP",
@@ -303,6 +359,53 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'dest': 'flow_ctrl',
'choices': FLOW_CTRL_DICT}),
+ SRC_IPV4: ArgumentPack(['--src'],
+ {'help': 'Configure source IPv4 address',
+ 'dest': 'src_ipv4',
+ 'required': True,
+ 'type': check_ipv4_addr}),
+
+ DST_IPV4: ArgumentPack(['--dst'],
+ {'help': 'Configure destination IPv4 address',
+ 'dest': 'dst_ipv4',
+ 'required': True,
+ 'type': check_ipv4_addr}),
+
+
+ DST_MAC: ArgumentPack(['--dst'],
+ {'help': 'Configure destination MAC address',
+ 'dest': 'dst_mac',
+ 'required': True,
+ 'type': check_mac_addr}),
+
+ RETRIES: ArgumentPack(['-r', '--retries'],
+ {'help': 'retries count [default is zero]',
+ 'dest': 'retries',
+ 'default': 0,
+ 'type': int}),
+
+
+ OUTPUT_FILENAME: ArgumentPack(['-o', '--output'],
+ {'help': 'Output PCAP filename',
+ 'dest': 'output_filename',
+ 'default': None,
+ 'required': True,
+ 'type': str}),
+
+
+ PORT_RESTART: ArgumentPack(['-r', '--restart'],
+ {'help': 'hard restart port(s)',
+ 'dest': 'restart',
+ 'default': False,
+ 'action': 'store_true'}),
+
+ LIMIT: ArgumentPack(['-l', '--limit'],
+ {'help': 'Limit the packet count to be written to the file',
+ 'dest': 'limit',
+ 'default': 1000,
+ 'type': int}),
+
+
SUPPORTED: ArgumentPack(['--supp'],
{'help': 'Show which attributes are supported by current NICs',
'default': None,
@@ -325,6 +428,33 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'help': "A list of ports on which to apply the command",
'default': []}),
+
+ SINGLE_PORT: ArgumentPack(['--port', '-p'],
+ {'dest':'ports',
+ 'type': int,
+ 'metavar': 'PORT',
+ 'help': 'source port for the action',
+ 'required': True}),
+
+ PING_IPV4: ArgumentPack(['-d'],
+ {'help': 'which IPv4 to ping',
+ 'dest': 'ping_ipv4',
+ 'required': True,
+ 'type': check_ipv4_addr}),
+
+ PING_COUNT: ArgumentPack(['-n', '--count'],
+ {'help': 'How many times to ping [default is 5]',
+ 'dest': 'count',
+ 'default': 5,
+ 'type': int}),
+
+ PKT_SIZE: ArgumentPack(['-s'],
+ {'dest':'pkt_size',
+ 'help': 'packet size to use',
+ 'default': 64,
+ 'type': check_pkt_size}),
+
+
ALL_PORTS: ArgumentPack(['-a'],
{"action": "store_true",
"dest": "all_ports",
@@ -362,6 +492,14 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'type': is_valid_file,
'help': "File path to load"}),
+ FILE_PATH_NO_CHECK: ArgumentPack(['-f'],
+ {'metavar': 'FILE',
+ 'dest': 'file',
+ 'nargs': 1,
+ 'required': True,
+ 'type': str,
+ 'help': "File path to load"}),
+
FILE_FROM_DB: ArgumentPack(['--db'],
{'metavar': 'LOADED_STREAM_PACK',
'help': "A stream pack which already loaded into console cache."}),
@@ -447,11 +585,18 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'default': None,
'help': "Core mask - only cores responding to the bit mask will be active"}),
+ SERVICE_OFF: ArgumentPack(['--off'],
+ {'action': 'store_false',
+ 'dest': 'enabled',
+ 'default': True,
+ 'help': 'Deactivates services on port(s)'}),
+
# advanced options
PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST,
ALL_PORTS],
{'required': False}),
+
STREAM_FROM_PATH_OR_FILE: ArgumentGroup(MUTEX, [FILE_PATH,
FILE_FROM_DB],
{'required': True}),
@@ -515,6 +660,8 @@ class CCmdArgParser(argparse.ArgumentParser):
if not self.has_ports_cfg(opts):
return opts
+ opts.ports = listify(opts.ports)
+
# if all ports are marked or
if (getattr(opts, "all_ports", None) == True) or (getattr(opts, "ports", None) == []):
if default_ports is None:
@@ -522,10 +669,17 @@ class CCmdArgParser(argparse.ArgumentParser):
else:
opts.ports = default_ports
+ opts.ports = list_remove_dup(opts.ports)
+
# so maybe we have ports configured
invalid_ports = list_difference(opts.ports, self.stateless_client.get_all_ports())
if invalid_ports:
- msg = "{0}: port(s) {1} are not valid port IDs".format(self.cmd_name, invalid_ports)
+
+ if len(invalid_ports) > 1:
+ msg = "{0}: port(s) {1} are not valid port IDs".format(self.cmd_name, invalid_ports)
+ else:
+ msg = "{0}: port {1} is not a valid port ID".format(self.cmd_name, invalid_ports[0])
+
self.stateless_client.logger.log(format_text(msg, 'bold'))
return RC_ERR(msg)
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py
index bfb96950..63b05bf4 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py
@@ -27,6 +27,9 @@ class TextCodesStripper:
def strip (s):
return re.sub(TextCodesStripper.pattern, '', s)
+def clear_formatting(s):
+ return TextCodesStripper.strip(s)
+
def format_num (size, suffix = "", compact = True, opts = None):
if opts is None:
opts = ()
@@ -128,11 +131,13 @@ def yellow(text):
def underline(text):
return text_attribute(text, 'underline')
-
+# apply attribute on each non-empty line
def text_attribute(text, attribute):
- return "{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'],
- txt=text,
- stop=TEXT_CODES[attribute]['end'])
+ return '\n'.join(['{start}{txt}{end}'.format(
+ start = TEXT_CODES[attribute]['start'],
+ txt = line,
+ end = TEXT_CODES[attribute]['end'])
+ if line else '' for line in ('%s' % text).split('\n')])
FUNC_DICT = {'blue': blue,
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py
index 397ada16..a2a47927 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py
@@ -6,7 +6,7 @@ class ZippedMsg:
MSG_COMPRESS_THRESHOLD = 256
MSG_COMPRESS_HEADER_MAGIC = 0xABE85CEA
- def check_threshold (self, msg):
+ def check_threshold(self, msg):
return len(msg) >= self.MSG_COMPRESS_THRESHOLD
def compress (self, msg):
@@ -16,7 +16,7 @@ class ZippedMsg:
return new_msg
- def decompress (self, msg):
+ def decompress(self, msg):
if len(msg) < 8:
return None
@@ -30,3 +30,15 @@ class ZippedMsg:
return x
+
+ def is_compressed(self, msg):
+ if len(msg) < 8:
+ return False
+
+ t = struct.unpack(">II", msg[:8])
+ if (t[0] != self.MSG_COMPRESS_HEADER_MAGIC):
+ return False
+
+ return True
+
+
diff --git a/scripts/dpdk_nic_bind.py b/scripts/dpdk_nic_bind.py
index 36d123f1..e797666b 100755
--- a/scripts/dpdk_nic_bind.py
+++ b/scripts/dpdk_nic_bind.py
@@ -54,7 +54,11 @@ if needed_path not in PATH:
# Each device within this is itself a dictionary of device properties
devices = {}
# list of supported DPDK drivers
-dpdk_drivers = [ "igb_uio", "vfio-pci", "uio_pci_generic" ]
+# ,
+
+dpdk_and_kernel=[ "mlx5_core", "mlx5_ib" ]
+
+dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic" ]
# command-line arg flags
b_flag = None
@@ -568,10 +572,15 @@ def show_status():
if not has_driver(d):
no_drv.append(devices[d])
continue
- if devices[d]["Driver_str"] in dpdk_drivers:
+
+ if devices[d]["Driver_str"] in dpdk_and_kernel:
dpdk_drv.append(devices[d])
- else:
kernel_drv.append(devices[d])
+ else:
+ if devices[d]["Driver_str"] in dpdk_drivers:
+ dpdk_drv.append(devices[d])
+ else:
+ kernel_drv.append(devices[d])
# print each category separately, so we can clearly see what's used by DPDK
display_devices("Network devices using DPDK-compatible driver", dpdk_drv, \
@@ -617,7 +626,7 @@ def show_table(get_macs = True):
get_nic_details()
dpdk_drv = []
for d in devices.keys():
- if devices[d].get("Driver_str") in dpdk_drivers:
+ if devices[d].get("Driver_str") in (dpdk_drivers+dpdk_and_kernel):
dpdk_drv.append(d)
if get_macs:
diff --git a/scripts/dpdk_setup_ports.py b/scripts/dpdk_setup_ports.py
index f85dae5d..ce6d2b2f 100755
--- a/scripts/dpdk_setup_ports.py
+++ b/scripts/dpdk_setup_ports.py
@@ -14,6 +14,7 @@ import traceback
from collections import defaultdict, OrderedDict
from distutils.util import strtobool
import getpass
+import subprocess
class ConfigCreator(object):
mandatory_interface_fields = ['Slot_str', 'Device_str', 'NUMA']
@@ -41,51 +42,52 @@ class ConfigCreator(object):
cores[core] = cores[core][:1]
include_lcores = [int(x) for x in include_lcores]
exclude_lcores = [int(x) for x in exclude_lcores]
+
self.has_zero_lcore = False
+ self.lcores_per_numa = {}
+ total_lcores = 0
for numa, cores in self.cpu_topology.items():
+ self.lcores_per_numa[numa] = {'main': [], 'siblings': [], 'all': []}
for core, lcores in cores.items():
- for lcore in copy.copy(lcores):
+ total_lcores += len(lcores)
+ for lcore in list(lcores):
if include_lcores and lcore not in include_lcores:
cores[core].remove(lcore)
if exclude_lcores and lcore in exclude_lcores:
cores[core].remove(lcore)
if 0 in lcores:
self.has_zero_lcore = True
- cores[core].remove(0)
- zero_lcore_numa = numa
- zero_lcore_core = core
- zero_lcore_siblings = cores[core]
- if self.has_zero_lcore:
- del self.cpu_topology[zero_lcore_numa][zero_lcore_core]
- self.cpu_topology[zero_lcore_numa][zero_lcore_core] = zero_lcore_siblings
+ lcores.remove(0)
+ self.lcores_per_numa[numa]['siblings'].extend(lcores)
+ else:
+ self.lcores_per_numa[numa]['main'].extend(lcores[:1])
+ self.lcores_per_numa[numa]['siblings'].extend(lcores[1:])
+ self.lcores_per_numa[numa]['all'].extend(lcores)
+
for interface in self.interfaces:
for mandatory_interface_field in ConfigCreator.mandatory_interface_fields:
if mandatory_interface_field not in interface:
raise DpdkSetup("Expected '%s' field in interface dictionary, got: %s" % (mandatory_interface_field, interface))
+
Device_str = self._verify_devices_same_type(self.interfaces)
if '40Gb' in Device_str:
self.speed = 40
else:
self.speed = 10
- lcores_per_numa = OrderedDict()
- system_lcores = int(self.has_zero_lcore)
- for numa, core in self.cpu_topology.items():
- for lcores in core.values():
- if numa not in lcores_per_numa:
- lcores_per_numa[numa] = []
- lcores_per_numa[numa].extend(lcores)
- system_lcores += len(lcores)
- minimum_required_lcores = len(self.interfaces) / 2 + 2
- if system_lcores < minimum_required_lcores:
+
+ minimum_required_lcores = len(self.interfaces) // 2 + 2
+ if total_lcores < minimum_required_lcores:
raise DpdkSetup('Your system should have at least %s cores for %s interfaces, and it has: %s.' %
- (minimum_required_lcores, len(self.interfaces), system_lcores + (0 if self.has_zero_lcore else 1)))
+ (minimum_required_lcores, len(self.interfaces), total_lcores))
interfaces_per_numa = defaultdict(int)
+
for i in range(0, len(self.interfaces), 2):
- if self.interfaces[i]['NUMA'] != self.interfaces[i+1]['NUMA'] and not ignore_numa:
+ numa = self.interfaces[i]['NUMA']
+ if numa != self.interfaces[i+1]['NUMA'] and not ignore_numa:
raise DpdkSetup('NUMA of each pair of interfaces should be the same. Got NUMA %s for client interface %s, NUMA %s for server interface %s' %
- (self.interfaces[i]['NUMA'], self.interfaces[i]['Slot_str'], self.interfaces[i+1]['NUMA'], self.interfaces[i+1]['Slot_str']))
- interfaces_per_numa[self.interfaces[i]['NUMA']] += 2
- self.lcores_per_numa = lcores_per_numa
+ (numa, self.interfaces[i]['Slot_str'], self.interfaces[i+1]['NUMA'], self.interfaces[i+1]['Slot_str']))
+ interfaces_per_numa[numa] += 2
+
self.interfaces_per_numa = interfaces_per_numa
self.prefix = prefix
self.zmq_pub_port = zmq_pub_port
@@ -153,16 +155,20 @@ class ConfigCreator(object):
config_str += ' '*8 + 'src_mac: %s\n' % self.verify_mac(interface['src_mac'])
if index % 2:
config_str += '\n' # dual if barrier
+
if not self.ignore_numa:
config_str += ' platform:\n'
- if len(self.interfaces_per_numa.keys()) == 1 and -1 in self.interfaces_per_numa: # VM, use any cores, 1 core per dual_if
- lcores_pool = sorted([lcore for lcores in self.lcores_per_numa.values() for lcore in lcores])
- config_str += ' '*6 + 'master_thread_id: %s\n' % (0 if self.has_zero_lcore else lcores_pool.pop())
+ if len(self.interfaces_per_numa.keys()) == 1 and -1 in self.interfaces_per_numa: # VM, use any cores
+ lcores_pool = sorted([lcore for lcores in self.lcores_per_numa.values() for lcore in lcores['all']])
+ config_str += ' '*6 + 'master_thread_id: %s\n' % (0 if self.has_zero_lcore else lcores_pool.pop(0))
config_str += ' '*6 + 'latency_thread_id: %s\n' % lcores_pool.pop(0)
- lcores_per_dual_if = int(len(lcores_pool) / len(self.interfaces))
+ lcores_per_dual_if = int(len(lcores_pool) * 2 / len(self.interfaces))
config_str += ' '*6 + 'dual_if:\n'
for i in range(0, len(self.interfaces), 2):
- lcores_for_this_dual_if = [str(lcores_pool.pop(0)) for _ in range(lcores_per_dual_if)]
+ lcores_for_this_dual_if = list(map(str, sorted(lcores_pool[:lcores_per_dual_if])))
+ lcores_pool = lcores_pool[lcores_per_dual_if:]
+ if not lcores_for_this_dual_if:
+ raise DpdkSetup('lcores_for_this_dual_if is empty (internal bug, please report with details of setup)')
config_str += ' '*8 + '- socket: 0\n'
config_str += ' '*10 + 'threads: [%s]\n\n' % ','.join(lcores_for_this_dual_if)
else:
@@ -170,26 +176,46 @@ class ConfigCreator(object):
lcores_per_dual_if = 99
extra_lcores = 1 if self.has_zero_lcore else 2
# worst case 3 iterations, to ensure master and "rx" have cores left
- while (lcores_per_dual_if * sum(self.interfaces_per_numa.values()) / 2) + extra_lcores > sum([len(lcores) for lcores in self.lcores_per_numa.values()]):
+ while (lcores_per_dual_if * sum(self.interfaces_per_numa.values()) / 2) + extra_lcores > sum([len(lcores['all']) for lcores in self.lcores_per_numa.values()]):
lcores_per_dual_if -= 1
- for numa, cores in self.lcores_per_numa.items():
+ for numa, lcores_dict in self.lcores_per_numa.items():
if not self.interfaces_per_numa[numa]:
continue
- lcores_per_dual_if = min(lcores_per_dual_if, int(2 * len(cores) / self.interfaces_per_numa[numa]))
+ lcores_per_dual_if = min(lcores_per_dual_if, int(2 * len(lcores_dict['all']) / self.interfaces_per_numa[numa]))
lcores_pool = copy.deepcopy(self.lcores_per_numa)
# first, allocate lcores for dual_if section
dual_if_section = ' '*6 + 'dual_if:\n'
for i in range(0, len(self.interfaces), 2):
numa = self.interfaces[i]['NUMA']
dual_if_section += ' '*8 + '- socket: %s\n' % numa
- lcores_for_this_dual_if = [str(lcores_pool[numa].pop(0)) for _ in range(lcores_per_dual_if)]
+ lcores_for_this_dual_if = lcores_pool[numa]['all'][:lcores_per_dual_if]
+ lcores_pool[numa]['all'] = lcores_pool[numa]['all'][lcores_per_dual_if:]
+ for lcore in lcores_for_this_dual_if:
+ if lcore in lcores_pool[numa]['main']:
+ lcores_pool[numa]['main'].remove(lcore)
+ elif lcore in lcores_pool[numa]['siblings']:
+ lcores_pool[numa]['siblings'].remove(lcore)
+ else:
+ raise DpdkSetup('lcore not in main nor in siblings list (internal bug, please report with details of setup)')
if not lcores_for_this_dual_if:
raise DpdkSetup('Not enough cores at NUMA %s. This NUMA has %s processing units and %s interfaces.' % (numa, len(self.lcores_per_numa[numa]), self.interfaces_per_numa[numa]))
- dual_if_section += ' '*10 + 'threads: [%s]\n\n' % ','.join(lcores_for_this_dual_if)
+ dual_if_section += ' '*10 + 'threads: [%s]\n\n' % ','.join(list(map(str, sorted(lcores_for_this_dual_if))))
+
# take the cores left to master and rx
- lcores_pool_left = [lcore for lcores in lcores_pool.values() for lcore in lcores]
- config_str += ' '*6 + 'master_thread_id: %s\n' % (0 if self.has_zero_lcore else lcores_pool_left.pop(0))
- config_str += ' '*6 + 'latency_thread_id: %s\n' % lcores_pool_left.pop(0)
+ mains_left = [lcore for lcores in lcores_pool.values() for lcore in lcores['main']]
+ siblings_left = [lcore for lcores in lcores_pool.values() for lcore in lcores['siblings']]
+ if mains_left:
+ rx_core = mains_left.pop(0)
+ else:
+ rx_core = siblings_left.pop(0)
+ if self.has_zero_lcore:
+ master_core = 0
+ elif mains_left:
+ master_core = mains_left.pop(0)
+ else:
+ master_core = siblings_left.pop(0)
+ config_str += ' '*6 + 'master_thread_id: %s\n' % master_core
+ config_str += ' '*6 + 'latency_thread_id: %s\n' % rx_core
# add the dual_if section
config_str += dual_if_section
@@ -227,6 +253,7 @@ class CIfMap:
self.m_cfg_file =cfg_file;
self.m_cfg_dict={};
self.m_devices={};
+ self.m_is_mellanox_mode=False;
def dump_error (self,err):
s="""%s
@@ -264,6 +291,94 @@ Other network devices
s= self.dump_error (err)
raise DpdkSetup(s)
+ def set_only_mellanox_nics(self):
+ self.m_is_mellanox_mode=True;
+
+ def get_only_mellanox_nics(self):
+ return self.m_is_mellanox_mode
+
+
+ def read_pci (self,pci_id,reg_id):
+ out=subprocess.check_output(['setpci', '-s',pci_id, '%s.w' %(reg_id)])
+ out=out.decode(errors='replace');
+ return (out.strip());
+
+ def write_pci (self,pci_id,reg_id,val):
+ out=subprocess.check_output(['setpci','-s',pci_id, '%s.w=%s' %(reg_id,val)])
+ out=out.decode(errors='replace');
+ return (out.strip());
+
+ def tune_mlx5_device (self,pci_id):
+ # set PCIe Read to 1024 and not 512 ... need to add it to startup s
+ val=self.read_pci (pci_id,68)
+ if val[0]!='3':
+ val='3'+val[1:]
+ self.write_pci (pci_id,68,val)
+ assert(self.read_pci (pci_id,68)==val);
+
+ def get_mtu_mlx5 (self,dev_id):
+ if len(dev_id)>0:
+ out=subprocess.check_output(['ifconfig', dev_id])
+ out=out.decode(errors='replace');
+ obj=re.search(r'MTU:(\d+)',out,flags=re.MULTILINE|re.DOTALL);
+ if obj:
+ return int(obj.group(1));
+ else:
+ obj=re.search(r'mtu (\d+)',out,flags=re.MULTILINE|re.DOTALL);
+ if obj:
+ return int(obj.group(1));
+ else:
+ return -1
+
+ def set_mtu_mlx5 (self,dev_id,new_mtu):
+ if len(dev_id)>0:
+ out=subprocess.check_output(['ifconfig', dev_id,'mtu',str(new_mtu)])
+ out=out.decode(errors='replace');
+
+
+ def set_max_mtu_mlx5_device(self,dev_id):
+ mtu=9*1024+22
+ dev_mtu=self.get_mtu_mlx5 (dev_id);
+ if (dev_mtu>0) and (dev_mtu!=mtu):
+ self.set_mtu_mlx5(dev_id,mtu);
+ if self.get_mtu_mlx5(dev_id) != mtu:
+ print("Could not set MTU to %d" % mtu)
+ exit(-1);
+
+
+ def disable_flow_control_mlx5_device (self,dev_id):
+
+ if len(dev_id)>0:
+ my_stderr = open("/dev/null","wb")
+ cmd ='ethtool -A '+dev_id + ' rx off tx off '
+ subprocess.call(cmd, stdout=my_stderr,stderr=my_stderr, shell=True)
+ my_stderr.close();
+
+ def check_ofe_version (self):
+ ofed_info='/usr/bin/ofed_info'
+ ofed_ver= '-3.4-'
+ ofed_ver_show= '3.4-1'
+
+
+ if not os.path.isfile(ofed_info):
+ print("OFED %s is not installed on this setup" % ofed_info)
+ exit(-1);
+
+ try:
+ out = subprocess.check_output([ofed_info])
+ except Exception as e:
+ print("OFED %s can't run " % (ofed_info))
+ exit(-1);
+
+ lines=out.splitlines();
+
+ if len(lines)>1:
+ if not (ofed_ver in str(lines[0])):
+ print("installed OFED version is '%s' should be at least '%s' and up" % (lines[0],ofed_ver_show))
+ exit(-1);
+
+
+
def load_config_file (self):
fcfg=self.m_cfg_file
@@ -299,15 +414,19 @@ Other network devices
self.raise_error ('Error: port_limit should not be higher than number of interfaces in config file: %s\n' % fcfg)
- def do_bind_one (self,key):
- cmd='%s dpdk_nic_bind.py --bind=igb_uio %s ' % (sys.executable, key)
+ def do_bind_one (self,key,mellanox):
+ if mellanox:
+ drv="mlx5_core"
+ else:
+ drv="igb_uio"
+
+ cmd='%s dpdk_nic_bind.py --bind=%s %s ' % (sys.executable, drv,key)
print(cmd)
res=os.system(cmd);
if res!=0:
raise DpdkSetup('')
-
def pci_name_to_full_name (self,pci_name):
c='[0-9A-Fa-f]';
sp='[:]'
@@ -330,7 +449,7 @@ Other network devices
dpdk_nic_bind.get_nic_details()
self.m_devices= dpdk_nic_bind.devices
- def do_run (self):
+ def do_run (self,only_check_all_mlx=False):
self.run_dpdk_lspci ()
if map_driver.dump_interfaces is None or (map_driver.dump_interfaces == [] and map_driver.parent_cfg):
self.load_config_file()
@@ -343,27 +462,74 @@ Other network devices
if_list.append(dev['Slot'])
if_list = list(map(self.pci_name_to_full_name, if_list))
+
+
+ # check how many mellanox cards we have
+ Mellanox_cnt=0;
for key in if_list:
if key not in self.m_devices:
err=" %s does not exist " %key;
raise DpdkSetup(err)
+ if 'Vendor_str' not in self.m_devices[key]:
+ err=" %s does not have Vendor_str " %key;
+ raise DpdkSetup(err)
- if 'Driver_str' in self.m_devices[key]:
- if self.m_devices[key]['Driver_str'] not in dpdk_nic_bind.dpdk_drivers :
- self.do_bind_one (key)
+ if self.m_devices[key]['Vendor_str'].find("Mellanox")>-1 :
+ Mellanox_cnt=Mellanox_cnt+1
+
+
+ if not map_driver.dump_interfaces :
+ if ((Mellanox_cnt>0) and (Mellanox_cnt!= len(if_list))):
+ err=" All driver should be from one vendor. you have at least one driver from Mellanox but not all ";
+ raise DpdkSetup(err)
+
+
+ if not map_driver.dump_interfaces :
+ if Mellanox_cnt>0 :
+ self.set_only_mellanox_nics()
+
+ if self.get_only_mellanox_nics():
+ self.check_ofe_version ()
+ for key in if_list:
+ pci_id=self.m_devices[key]['Slot_str']
+ self.tune_mlx5_device (pci_id)
+ if 'Interface' in self.m_devices[key]:
+ dev_id=self.m_devices[key]['Interface']
+ self.disable_flow_control_mlx5_device (dev_id)
+ self.set_max_mtu_mlx5_device(dev_id)
+
+
+ if only_check_all_mlx:
+ if Mellanox_cnt >0:
+ exit(1);
else:
- self.do_bind_one (key)
+ exit(0);
- if if_list and map_driver.args.parent and dpdk_nic_bind.get_igb_uio_usage():
- pid = dpdk_nic_bind.get_pid_using_pci(if_list)
- if pid:
- cmdline = dpdk_nic_bind.read_pid_cmdline(pid)
- print('Some or all of given interfaces are in use by following process:\npid: %s, cmd: %s' % (pid, cmdline))
- if not dpdk_nic_bind.confirm('Ignore and proceed (y/N):'):
- sys.exit(1)
+ for key in if_list:
+ if key not in self.m_devices:
+ err=" %s does not exist " %key;
+ raise DpdkSetup(err)
+
+ if 'Driver_str' in self.m_devices[key]:
+ if self.m_devices[key]['Driver_str'] not in (dpdk_nic_bind.dpdk_drivers+dpdk_nic_bind.dpdk_and_kernel) :
+ self.do_bind_one (key,(Mellanox_cnt>0))
+ pass;
else:
- print('WARNING: Some other program is using DPDK driver.\nIf it is TRex and you did not configure it for dual run, current command will fail.')
+ self.do_bind_one (key,(Mellanox_cnt>0))
+ pass;
+
+ if (Mellanox_cnt==0):
+ # We are not in Mellanox case, we can do this check only in case of Intel (another process is running)
+ if if_list and map_driver.args.parent and (dpdk_nic_bind.get_igb_uio_usage()):
+ pid = dpdk_nic_bind.get_pid_using_pci(if_list)
+ if pid:
+ cmdline = dpdk_nic_bind.read_pid_cmdline(pid)
+ print('Some or all of given interfaces are in use by following process:\npid: %s, cmd: %s' % (pid, cmdline))
+ if not dpdk_nic_bind.confirm('Ignore and proceed (y/N):'):
+ sys.exit(1)
+ else:
+ print('WARNING: Some other program is using DPDK driver.\nIf it is TRex and you did not configure it for dual run, current command will fail.')
def do_return_to_linux(self):
if not self.m_devices:
@@ -684,7 +850,7 @@ To return to Linux the DPDK bound interfaces (for ifconfig etc.)
sudo ./dpdk_set_ports.py -l
To create TRex config file using interactive mode
- sudo ./dpdk_set_ports.py -l
+ sudo ./dpdk_set_ports.py -i
To create a default config file (example1)
sudo ./dpdk_setup_ports.py -c 02:00.0 02:00.1 -o /etc/trex_cfg.yaml
@@ -829,6 +995,8 @@ def main ():
print(e)
exit(-1)
+
+
if __name__ == '__main__':
main()
diff --git a/scripts/dumy_libs/libibverbs.so.1 b/scripts/dumy_libs/libibverbs.so.1
new file mode 100644
index 00000000..bd93569d
--- /dev/null
+++ b/scripts/dumy_libs/libibverbs.so.1
Binary files differ
diff --git a/scripts/dumy_libs/libnl-3.so.200 b/scripts/dumy_libs/libnl-3.so.200
new file mode 100644
index 00000000..9eaaac9d
--- /dev/null
+++ b/scripts/dumy_libs/libnl-3.so.200
Binary files differ
diff --git a/scripts/dumy_libs/libnl-route-3.so.200 b/scripts/dumy_libs/libnl-route-3.so.200
new file mode 100644
index 00000000..9e1835f1
--- /dev/null
+++ b/scripts/dumy_libs/libnl-route-3.so.200
Binary files differ
diff --git a/scripts/exp/dns_ipv6_rxcheck-ex.erf b/scripts/exp/dns_ipv6_rxcheck-ex.erf
index ebf95197..3984c0ef 100755
--- a/scripts/exp/dns_ipv6_rxcheck-ex.erf
+++ b/scripts/exp/dns_ipv6_rxcheck-ex.erf
Binary files differ
diff --git a/scripts/exp/dns_ipv6_rxcheck.erf b/scripts/exp/dns_ipv6_rxcheck.erf
index ebf95197..3984c0ef 100644
--- a/scripts/exp/dns_ipv6_rxcheck.erf
+++ b/scripts/exp/dns_ipv6_rxcheck.erf
Binary files differ
diff --git a/scripts/exp/dns_rxcheck-ex.erf b/scripts/exp/dns_rxcheck-ex.erf
index 21a610a6..a6135f34 100755
--- a/scripts/exp/dns_rxcheck-ex.erf
+++ b/scripts/exp/dns_rxcheck-ex.erf
Binary files differ
diff --git a/scripts/exp/dns_rxcheck.erf b/scripts/exp/dns_rxcheck.erf
index 21a610a6..a6135f34 100644
--- a/scripts/exp/dns_rxcheck.erf
+++ b/scripts/exp/dns_rxcheck.erf
Binary files differ
diff --git a/scripts/exp/ipv6-0-ex.erf b/scripts/exp/ipv6-0-ex.erf
index 21e0eabb..1e102856 100755
--- a/scripts/exp/ipv6-0-ex.erf
+++ b/scripts/exp/ipv6-0-ex.erf
Binary files differ
diff --git a/scripts/exp/ipv6-0.erf b/scripts/exp/ipv6-0.erf
index 21e0eabb..1e102856 100644
--- a/scripts/exp/ipv6-0.erf
+++ b/scripts/exp/ipv6-0.erf
Binary files differ
diff --git a/scripts/exp/ipv6_vlan-0-ex.erf b/scripts/exp/ipv6_vlan-0-ex.erf
index b97d760b..f7c82833 100755
--- a/scripts/exp/ipv6_vlan-0-ex.erf
+++ b/scripts/exp/ipv6_vlan-0-ex.erf
Binary files differ
diff --git a/scripts/exp/ipv6_vlan-0.erf b/scripts/exp/ipv6_vlan-0.erf
index b97d760b..f7c82833 100644
--- a/scripts/exp/ipv6_vlan-0.erf
+++ b/scripts/exp/ipv6_vlan-0.erf
Binary files differ
diff --git a/scripts/exp/rtsp_short1_ipv6_rxcheck-ex.erf b/scripts/exp/rtsp_short1_ipv6_rxcheck-ex.erf
index 06ac6484..a35e9f47 100755
--- a/scripts/exp/rtsp_short1_ipv6_rxcheck-ex.erf
+++ b/scripts/exp/rtsp_short1_ipv6_rxcheck-ex.erf
Binary files differ
diff --git a/scripts/exp/rtsp_short1_ipv6_rxcheck.erf b/scripts/exp/rtsp_short1_ipv6_rxcheck.erf
index 06ac6484..a35e9f47 100644
--- a/scripts/exp/rtsp_short1_ipv6_rxcheck.erf
+++ b/scripts/exp/rtsp_short1_ipv6_rxcheck.erf
Binary files differ
diff --git a/scripts/exp/rtsp_short1_rxcheck-ex.erf b/scripts/exp/rtsp_short1_rxcheck-ex.erf
index cd120df9..cfc3726a 100755
--- a/scripts/exp/rtsp_short1_rxcheck-ex.erf
+++ b/scripts/exp/rtsp_short1_rxcheck-ex.erf
Binary files differ
diff --git a/scripts/exp/rtsp_short1_rxcheck.erf b/scripts/exp/rtsp_short1_rxcheck.erf
index cd120df9..cfc3726a 100644
--- a/scripts/exp/rtsp_short1_rxcheck.erf
+++ b/scripts/exp/rtsp_short1_rxcheck.erf
Binary files differ
diff --git a/scripts/external_libs/pyzmq-14.5.0/python2/cel59/32bit/zmq/sugar/context.py b/scripts/external_libs/pyzmq-14.5.0/python2/cel59/32bit/zmq/sugar/context.py
index 86a9c5dc..692e0256 100644
--- a/scripts/external_libs/pyzmq-14.5.0/python2/cel59/32bit/zmq/sugar/context.py
+++ b/scripts/external_libs/pyzmq-14.5.0/python2/cel59/32bit/zmq/sugar/context.py
@@ -5,7 +5,6 @@
# Distributed under the terms of the Modified BSD License.
import atexit
-import weakref
from zmq.backend import Context as ContextBase
from . import constants
@@ -14,6 +13,14 @@ from .constants import ENOTSUP, ctx_opt_names
from .socket import Socket
from zmq.error import ZMQError
+# notice when exiting, to avoid triggering term on exit
+_exiting = False
+def _notice_atexit():
+ global _exiting
+ _exiting = True
+atexit.register(_notice_atexit)
+
+
from zmq.utils.interop import cast_int_addr
@@ -25,7 +32,6 @@ class Context(ContextBase, AttributeSetter):
sockopts = None
_instance = None
_shadow = False
- _exiting = False
def __init__(self, io_threads=1, **kwargs):
super(Context, self).__init__(io_threads=io_threads, **kwargs)
@@ -35,18 +41,10 @@ class Context(ContextBase, AttributeSetter):
self._shadow = False
self.sockopts = {}
- self._exiting = False
- if not self._shadow:
- ctx_ref = weakref.ref(self)
- def _notify_atexit():
- ctx = ctx_ref()
- if ctx is not None:
- ctx._exiting = True
- atexit.register(_notify_atexit)
def __del__(self):
"""deleting a Context should terminate it, without trying non-threadsafe destroy"""
- if not self._shadow and not self._exiting:
+ if not self._shadow and not _exiting:
self.term()
def __enter__(self):
diff --git a/scripts/external_libs/pyzmq-14.5.0/python2/cel59/64bit/zmq/sugar/context.py b/scripts/external_libs/pyzmq-14.5.0/python2/cel59/64bit/zmq/sugar/context.py
index 86a9c5dc..692e0256 100644
--- a/scripts/external_libs/pyzmq-14.5.0/python2/cel59/64bit/zmq/sugar/context.py
+++ b/scripts/external_libs/pyzmq-14.5.0/python2/cel59/64bit/zmq/sugar/context.py
@@ -5,7 +5,6 @@
# Distributed under the terms of the Modified BSD License.
import atexit
-import weakref
from zmq.backend import Context as ContextBase
from . import constants
@@ -14,6 +13,14 @@ from .constants import ENOTSUP, ctx_opt_names
from .socket import Socket
from zmq.error import ZMQError
+# notice when exiting, to avoid triggering term on exit
+_exiting = False
+def _notice_atexit():
+ global _exiting
+ _exiting = True
+atexit.register(_notice_atexit)
+
+
from zmq.utils.interop import cast_int_addr
@@ -25,7 +32,6 @@ class Context(ContextBase, AttributeSetter):
sockopts = None
_instance = None
_shadow = False
- _exiting = False
def __init__(self, io_threads=1, **kwargs):
super(Context, self).__init__(io_threads=io_threads, **kwargs)
@@ -35,18 +41,10 @@ class Context(ContextBase, AttributeSetter):
self._shadow = False
self.sockopts = {}
- self._exiting = False
- if not self._shadow:
- ctx_ref = weakref.ref(self)
- def _notify_atexit():
- ctx = ctx_ref()
- if ctx is not None:
- ctx._exiting = True
- atexit.register(_notify_atexit)
def __del__(self):
"""deleting a Context should terminate it, without trying non-threadsafe destroy"""
- if not self._shadow and not self._exiting:
+ if not self._shadow and not _exiting:
self.term()
def __enter__(self):
diff --git a/scripts/external_libs/pyzmq-14.5.0/python2/fedora18/64bit/zmq/sugar/context.py b/scripts/external_libs/pyzmq-14.5.0/python2/fedora18/64bit/zmq/sugar/context.py
index 86a9c5dc..692e0256 100644
--- a/scripts/external_libs/pyzmq-14.5.0/python2/fedora18/64bit/zmq/sugar/context.py
+++ b/scripts/external_libs/pyzmq-14.5.0/python2/fedora18/64bit/zmq/sugar/context.py
@@ -5,7 +5,6 @@
# Distributed under the terms of the Modified BSD License.
import atexit
-import weakref
from zmq.backend import Context as ContextBase
from . import constants
@@ -14,6 +13,14 @@ from .constants import ENOTSUP, ctx_opt_names
from .socket import Socket
from zmq.error import ZMQError
+# notice when exiting, to avoid triggering term on exit
+_exiting = False
+def _notice_atexit():
+ global _exiting
+ _exiting = True
+atexit.register(_notice_atexit)
+
+
from zmq.utils.interop import cast_int_addr
@@ -25,7 +32,6 @@ class Context(ContextBase, AttributeSetter):
sockopts = None
_instance = None
_shadow = False
- _exiting = False
def __init__(self, io_threads=1, **kwargs):
super(Context, self).__init__(io_threads=io_threads, **kwargs)
@@ -35,18 +41,10 @@ class Context(ContextBase, AttributeSetter):
self._shadow = False
self.sockopts = {}
- self._exiting = False
- if not self._shadow:
- ctx_ref = weakref.ref(self)
- def _notify_atexit():
- ctx = ctx_ref()
- if ctx is not None:
- ctx._exiting = True
- atexit.register(_notify_atexit)
def __del__(self):
"""deleting a Context should terminate it, without trying non-threadsafe destroy"""
- if not self._shadow and not self._exiting:
+ if not self._shadow and not _exiting:
self.term()
def __enter__(self):
diff --git a/scripts/external_libs/pyzmq-14.5.0/python3/cel59/32bit/zmq/sugar/context.py b/scripts/external_libs/pyzmq-14.5.0/python3/cel59/32bit/zmq/sugar/context.py
index 86a9c5dc..692e0256 100644
--- a/scripts/external_libs/pyzmq-14.5.0/python3/cel59/32bit/zmq/sugar/context.py
+++ b/scripts/external_libs/pyzmq-14.5.0/python3/cel59/32bit/zmq/sugar/context.py
@@ -5,7 +5,6 @@
# Distributed under the terms of the Modified BSD License.
import atexit
-import weakref
from zmq.backend import Context as ContextBase
from . import constants
@@ -14,6 +13,14 @@ from .constants import ENOTSUP, ctx_opt_names
from .socket import Socket
from zmq.error import ZMQError
+# notice when exiting, to avoid triggering term on exit
+_exiting = False
+def _notice_atexit():
+ global _exiting
+ _exiting = True
+atexit.register(_notice_atexit)
+
+
from zmq.utils.interop import cast_int_addr
@@ -25,7 +32,6 @@ class Context(ContextBase, AttributeSetter):
sockopts = None
_instance = None
_shadow = False
- _exiting = False
def __init__(self, io_threads=1, **kwargs):
super(Context, self).__init__(io_threads=io_threads, **kwargs)
@@ -35,18 +41,10 @@ class Context(ContextBase, AttributeSetter):
self._shadow = False
self.sockopts = {}
- self._exiting = False
- if not self._shadow:
- ctx_ref = weakref.ref(self)
- def _notify_atexit():
- ctx = ctx_ref()
- if ctx is not None:
- ctx._exiting = True
- atexit.register(_notify_atexit)
def __del__(self):
"""deleting a Context should terminate it, without trying non-threadsafe destroy"""
- if not self._shadow and not self._exiting:
+ if not self._shadow and not _exiting:
self.term()
def __enter__(self):
diff --git a/scripts/external_libs/pyzmq-14.5.0/python3/cel59/64bit/zmq/sugar/context.py b/scripts/external_libs/pyzmq-14.5.0/python3/cel59/64bit/zmq/sugar/context.py
index 86a9c5dc..692e0256 100644
--- a/scripts/external_libs/pyzmq-14.5.0/python3/cel59/64bit/zmq/sugar/context.py
+++ b/scripts/external_libs/pyzmq-14.5.0/python3/cel59/64bit/zmq/sugar/context.py
@@ -5,7 +5,6 @@
# Distributed under the terms of the Modified BSD License.
import atexit
-import weakref
from zmq.backend import Context as ContextBase
from . import constants
@@ -14,6 +13,14 @@ from .constants import ENOTSUP, ctx_opt_names
from .socket import Socket
from zmq.error import ZMQError
+# notice when exiting, to avoid triggering term on exit
+_exiting = False
+def _notice_atexit():
+ global _exiting
+ _exiting = True
+atexit.register(_notice_atexit)
+
+
from zmq.utils.interop import cast_int_addr
@@ -25,7 +32,6 @@ class Context(ContextBase, AttributeSetter):
sockopts = None
_instance = None
_shadow = False
- _exiting = False
def __init__(self, io_threads=1, **kwargs):
super(Context, self).__init__(io_threads=io_threads, **kwargs)
@@ -35,18 +41,10 @@ class Context(ContextBase, AttributeSetter):
self._shadow = False
self.sockopts = {}
- self._exiting = False
- if not self._shadow:
- ctx_ref = weakref.ref(self)
- def _notify_atexit():
- ctx = ctx_ref()
- if ctx is not None:
- ctx._exiting = True
- atexit.register(_notify_atexit)
def __del__(self):
"""deleting a Context should terminate it, without trying non-threadsafe destroy"""
- if not self._shadow and not self._exiting:
+ if not self._shadow and not _exiting:
self.term()
def __enter__(self):
diff --git a/scripts/external_libs/pyzmq-14.5.0/python3/fedora18/64bit/zmq/sugar/context.py b/scripts/external_libs/pyzmq-14.5.0/python3/fedora18/64bit/zmq/sugar/context.py
index 86a9c5dc..692e0256 100644
--- a/scripts/external_libs/pyzmq-14.5.0/python3/fedora18/64bit/zmq/sugar/context.py
+++ b/scripts/external_libs/pyzmq-14.5.0/python3/fedora18/64bit/zmq/sugar/context.py
@@ -5,7 +5,6 @@
# Distributed under the terms of the Modified BSD License.
import atexit
-import weakref
from zmq.backend import Context as ContextBase
from . import constants
@@ -14,6 +13,14 @@ from .constants import ENOTSUP, ctx_opt_names
from .socket import Socket
from zmq.error import ZMQError
+# notice when exiting, to avoid triggering term on exit
+_exiting = False
+def _notice_atexit():
+ global _exiting
+ _exiting = True
+atexit.register(_notice_atexit)
+
+
from zmq.utils.interop import cast_int_addr
@@ -25,7 +32,6 @@ class Context(ContextBase, AttributeSetter):
sockopts = None
_instance = None
_shadow = False
- _exiting = False
def __init__(self, io_threads=1, **kwargs):
super(Context, self).__init__(io_threads=io_threads, **kwargs)
@@ -35,18 +41,10 @@ class Context(ContextBase, AttributeSetter):
self._shadow = False
self.sockopts = {}
- self._exiting = False
- if not self._shadow:
- ctx_ref = weakref.ref(self)
- def _notify_atexit():
- ctx = ctx_ref()
- if ctx is not None:
- ctx._exiting = True
- atexit.register(_notify_atexit)
def __del__(self):
"""deleting a Context should terminate it, without trying non-threadsafe destroy"""
- if not self._shadow and not self._exiting:
+ if not self._shadow and not _exiting:
self.term()
def __enter__(self):
diff --git a/scripts/ko/3.10.0-327.el7.x86_64/igb_uio.ko b/scripts/ko/3.10.0-327.el7.x86_64/igb_uio.ko
new file mode 100644
index 00000000..a85b9add
--- /dev/null
+++ b/scripts/ko/3.10.0-327.el7.x86_64/igb_uio.ko
Binary files differ
diff --git a/scripts/master_daemon.py b/scripts/master_daemon.py
index e4a2fd1c..855358ff 100755
--- a/scripts/master_daemon.py
+++ b/scripts/master_daemon.py
@@ -33,12 +33,11 @@ def get_trex_path():
def update_trex(package_path = 'http://trex-tgn.cisco.com/trex/release/latest'):
if not args.allow_update:
raise Exception('Updating server not allowed')
+ file_name = 'trex_package.tar.gz'
# getting new package
if package_path.startswith('http'):
- file_name = package_path.split('/')[-1]
ret_code, stdout, stderr = run_command('wget %s -O %s' % (package_path, os.path.join(tmp_dir, file_name)), timeout = 600)
else:
- file_name = os.path.basename(package_path)
ret_code, stdout, stderr = run_command('rsync -Lc %s %s' % (package_path, os.path.join(tmp_dir, file_name)), timeout = 300)
if ret_code:
raise Exception('Could not get requested package. Result: %s' % [ret_code, stdout, stderr])
@@ -163,6 +162,11 @@ def _check_path_under_current_or_temp(path):
if getpass.getuser() != 'root':
fail('Please run this program as root/with sudo')
+pid = os.getpid()
+ret = os.system('taskset -pc 0 %s' % pid)
+if ret:
+ fail('Could not set self affinity to core zero.')
+
daemon_actions = OrderedDict([('start', 'start the daemon'),
('stop', 'exit the daemon process'),
('show', 'prompt the status of daemon process (running / not running)'),
diff --git a/scripts/t-rex-64 b/scripts/t-rex-64
index d2388cfd..fc8318a5 100755
--- a/scripts/t-rex-64
+++ b/scripts/t-rex-64
@@ -25,6 +25,12 @@ done <<< "$($PYTHON dpdk_setup_ports.py --dump-pci-description)"
cd $(dirname $0)
export LD_LIBRARY_PATH=$PWD
+#Add dummy lib in case we don't find it, e.g. there is no OFED installed
+if ldd _$(basename $0) | grep "libibverbs.so" | grep -q "not found"; then
+export LD_LIBRARY_PATH=$PWD:$PWD/dumy_libs
+fi
+
+
if [ -t 0 ] && [ -t 1 ]; then
export is_tty=true
saveterm="$(stty -g)"
diff --git a/scripts/t-rex-64-debug-gdb b/scripts/t-rex-64-debug-gdb
index 1e019407..83ab82e0 100755
--- a/scripts/t-rex-64-debug-gdb
+++ b/scripts/t-rex-64-debug-gdb
@@ -1,4 +1,11 @@
#! /bin/bash
export LD_LIBRARY_PATH=`pwd`
+
+#Add dummy lib in case we don't find it, e.g. there is no OFED installed
+if ldd _t-rex-64 | grep "libibverbs.so" | grep -q "not found"; then
+export LD_LIBRARY_PATH=$PWD:$PWD/dumy_libs
+fi
+
+
/usr/bin/gdb --args ./_t-rex-64-debug $@
diff --git a/scripts/t-rex-64-valgrind b/scripts/t-rex-64-valgrind
new file mode 100755
index 00000000..d11491ba
--- /dev/null
+++ b/scripts/t-rex-64-valgrind
@@ -0,0 +1,69 @@
+#! /bin/bash
+if [ "$(id -u)" != 0 ]; then
+ echo 'Error: Please run as root (sudo etc.)'
+ exit -1
+fi
+
+INPUT_ARGS=${@//[–—]/-} # replace bizarre minuses with normal one
+
+./trex-cfg $INPUT_ARGS
+RESULT=$?
+if [ $RESULT -ne 0 ]; then
+ exit $RESULT
+fi
+
+pci_desc_re='^(\S+) - (.+)$'
+source find_python.sh
+while read line
+do
+ if [[ "$line" =~ $pci_desc_re ]]; then
+ pci_name="pci$(echo ${BASH_REMATCH[1]} | tr ':' '_' | tr '.' '_')" # make alphanumeric name
+ export $pci_name="${BASH_REMATCH[2]}"
+ fi
+done <<< "$($PYTHON dpdk_setup_ports.py --dump-pci-description)"
+
+cd $(dirname $0)
+export LD_LIBRARY_PATH=$PWD
+
+#Add dummy lib in case we don't find it, e.g. there is no OFED installed
+if ldd ./_t-rex-64 | grep "libibverbs.so" | grep -q "not found"; then
+export LD_LIBRARY_PATH=$PWD:$PWD/dumy_libs
+fi
+
+export VALGRIND_LIB=/auto/proj-pcube-b/apps/PL-b/tools/valgrind-dpdk/lib/valgrind
+export VALGRIND_BIN="/auto/proj-pcube-b/apps/PL-b/tools/valgrind-dpdk/bin/valgrind --leak-check=full --error-exitcode=1 --suppressions=valgrind.sup"
+export GLIBCXX_FORCE_NEW=1
+
+if [ -t 0 ] && [ -t 1 ]; then
+ export is_tty=true
+ saveterm="$(stty -g)"
+else
+ export is_tty=false
+fi
+
+# if we have a new core run optimized trex
+if grep -q avx /proc/cpuinfo ; then
+ $VALGRIND_BIN ./_t-rex-64 $INPUT_ARGS
+ RESULT=$?
+ if [ $RESULT -eq 132 ]; then
+ echo " WARNING this program is optimized for the new Intel processors. "
+ echo " try the ./t-rex-64-o application that should work for any Intel processor but might be slower. "
+ echo " try to run t-rex-64-o .. "
+
+ $VALGRIND_BIN ./_t-rex-64 $INPUT_ARGS
+ RESULT=$?
+ fi
+else
+ $VALGRIND_BIN ./_t-rex-64 $INPUT_ARGS
+ RESULT=$?
+fi
+
+if $is_tty; then
+ stty $saveterm
+fi
+
+if [ $RESULT -ne 0 ]; then
+ exit $RESULT
+fi
+
+
diff --git a/scripts/trex-cfg b/scripts/trex-cfg
index 714aea6c..c6f12a7e 100755
--- a/scripts/trex-cfg
+++ b/scripts/trex-cfg
@@ -55,13 +55,23 @@ if ! lsmod | grep -q igb_uio ; then
fi
else
echo "ERROR: We don't have precompiled igb_uio.ko module for your kernel version"
- echo "You can try compiling yourself, using the following commands:"
- echo "\$cd ko/src "
- echo "\$make "
- echo "\$make install "
- echo "\$cd - "
- echo "Then try to run Trex again"
- exit 1
+ echo Will try compiling automatically.
+ {
+ cd ko/src &&
+ make &&
+ make install &&
+ cd -
+ } &> /dev/null || {
+ echo Automatic compilation failed.
+ echo "You can try compiling yourself, using the following commands:"
+ echo "\$cd ko/src "
+ echo "\$make "
+ echo "\$make install "
+ echo "\$cd - "
+ echo "Then try to run TRex again"
+ exit 1
+ }
+ echo Success.
fi
fi
diff --git a/scripts/trex_show_threads.py b/scripts/trex_show_threads.py
index fabe6d68..1824d073 100755
--- a/scripts/trex_show_threads.py
+++ b/scripts/trex_show_threads.py
@@ -58,8 +58,8 @@ def isnum (x):
def find_trex_pid ():
procs = [x for x in os.listdir('/proc/') if isnum(x)]
for proc in procs:
- cmd = open('/proc/{0}/{1}'.format(proc, 'cmdline')).readline()
- if '_t-rex' in cmd:
+ cmd = open('/proc/{0}/{1}'.format(proc, 'comm')).readline()
+ if cmd.startswith('_t-rex-64'):
return proc
return None
diff --git a/scripts/valgrind.sup b/scripts/valgrind.sup
new file mode 100644
index 00000000..b6bcc883
--- /dev/null
+++ b/scripts/valgrind.sup
@@ -0,0 +1,57 @@
+{
+ DL issue
+ Memcheck:Cond
+ fun:index
+ fun:expand_dynamic_string_token
+ fun:fillin_rpath
+ fun:_dl_init_paths
+ fun:dl_main
+ fun:_dl_sysdep_start
+ fun:_dl_start_final
+ fun:_dl_start
+ obj:/lib/x86_64-linux-gnu/ld-2.19.so
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+}
+
+{
+ DPDK threads
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:calloc
+ fun:allocate_dtv
+ fun:_dl_allocate_tls
+ fun:allocate_stack
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:rte_eal_init
+ fun:_Z9main_testiPPc
+ fun:(below main)
+}
+
+{
+ DPDK interrupt thread
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:calloc
+ fun:allocate_dtv
+ fun:_dl_allocate_tls
+ fun:allocate_stack
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:rte_eal_intr_init
+ fun:rte_eal_init
+ fun:_Z9main_testiPPc
+ fun:(below main)
+}
+
+{
+ DPDK epoll ctl
+ Memcheck:Param
+ epoll_ctl(event)
+ fun:epoll_ctl
+ fun:eal_intr_thread_main
+ fun:start_thread
+ fun:clone
+}
+
diff --git a/src/bp_gtest.cpp b/src/bp_gtest.cpp
index ca514c88..11bd6235 100755
--- a/src/bp_gtest.cpp
+++ b/src/bp_gtest.cpp
@@ -196,7 +196,7 @@ public:
ports[i]=lpg->GenerateOneSourcePort();
}
}
-
+ CGlobalInfo::m_options.m_run_mode = CParserOption::RUN_MODE_BATCH;
lpt->start_generate_stateful(buf,CGlobalInfo::m_options.preview);
lpt->m_node_gen.DumpHist(stdout);
diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp
index 62e8d822..80297c32 100755
--- a/src/bp_sim.cpp
+++ b/src/bp_sim.cpp
@@ -160,8 +160,8 @@ uint64_t CPlatformSocketInfoNoConfig::get_cores_mask(){
int i;
int offset=0;
/* master */
- uint32_t res=1;
- uint32_t mask=(1<<(offset+1));
+ uint64_t res=1;
+ uint64_t mask=(1LL<<(offset+1));
for (i=0; i<(cores_number-1); i++) {
res |= mask ;
mask = mask <<1;
@@ -238,6 +238,13 @@ bool CPlatformSocketInfoConfig::init(){
}
}
+ if (m_threads_per_dual_if > m_max_threads_per_dual_if) {
+ printf("ERROR: Maximum threads in platform section of config file is %d, unable to run with -c %d.\n",
+ m_max_threads_per_dual_if, m_threads_per_dual_if);
+ printf("Please increase the pool in config or use lower -c.\n");
+ exit(1);
+ }
+
int j;
for (j=0; j<m_threads_per_dual_if; j++) {
@@ -381,14 +388,14 @@ uint64_t CPlatformSocketInfoConfig::get_cores_mask(){
printf(" ERROR phy threads can't be higher than 64 \n");
exit(1);
}
- mask |=(1<<i);
+ mask |=(1LL<<i);
}
}
- mask |=(1<<m_platform->m_master_thread);
+ mask |=(1LL<<m_platform->m_master_thread);
assert(m_platform->m_master_thread<64);
if (m_rx_is_enabled) {
- mask |=(1<<m_platform->m_rx_thread);
+ mask |=(1LL<<m_platform->m_rx_thread);
assert(m_platform->m_rx_thread<64);
}
return (mask);
@@ -1668,7 +1675,8 @@ void CFlowPktInfo::do_generate_new_mbuf_rxcheck(rte_mbuf_t * m,
IPv6Header * ipv6=(IPv6Header *)(mp1 + 14);
uint8_t save_header= ipv6->getNextHdr();
ipv6->setNextHdr(RX_CHECK_V6_OPT_TYPE);
- ipv6->setHopLimit(TTL_RESERVE_DUPLICATE);
+ ipv6->setHopLimit(TTL_RESERVE_DUPLICATE);
+ ipv6->setTrafficClass(ipv6->getTrafficClass()|TOS_TTL_RESERVE_DUPLICATE);
ipv6->setPayloadLen( ipv6->getPayloadLen() +
sizeof(CRx_check_header));
rxhdr->m_option_type = save_header;
@@ -1678,6 +1686,8 @@ void CFlowPktInfo::do_generate_new_mbuf_rxcheck(rte_mbuf_t * m,
ipv4->setHeaderLength(current_opt_len+opt_len);
ipv4->setTotalLength(ipv4->getTotalLength()+opt_len);
ipv4->setTimeToLive(TTL_RESERVE_DUPLICATE);
+ ipv4->setTOS(ipv4->getTOS()|TOS_TTL_RESERVE_DUPLICATE);
+
rxhdr->m_option_type = RX_CHECK_V4_OPT_TYPE;
rxhdr->m_option_len = RX_CHECK_V4_OPT_LEN;
}
@@ -2148,6 +2158,7 @@ void CCapFileFlowInfo::update_info(){
lp = GetPacket(1);
assert(lp);
lp->m_pkt_indication.setTTL(TTL_RESERVE_DUPLICATE);
+ lp->m_pkt_indication.setTOSReserve();
}
}
@@ -2234,6 +2245,9 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
pkt_indication.setTTL(TTL_RESERVE_DUPLICATE-4);
}
+ pkt_indication.clearTOSReserve();
+
+
// Validation for first packet in flow
if (is_fif) {
lpflow->flow_id = m_total_flows;
@@ -4419,7 +4433,7 @@ void CFlowGenListPerThread::stop_stateless_simulation_file(){
}
void CFlowGenListPerThread::start_stateless_daemon_simulation(){
-
+ CGlobalInfo::m_options.m_run_mode = CParserOption::RUN_MODE_INTERACTIVE;
m_cur_time_sec = 0;
/* if no pending CP messages - the core will simply be stuck forever */
@@ -4438,6 +4452,7 @@ bool CFlowGenListPerThread::set_stateless_next_node( CGenNodeStateless * cur_nod
void CFlowGenListPerThread::start_stateless_daemon(CPreviewMode &preview){
+ CGlobalInfo::m_options.m_run_mode = CParserOption::RUN_MODE_INTERACTIVE;
m_cur_time_sec = 0;
/* set per thread global info, for performance */
m_preview_mode = preview;
@@ -4562,6 +4577,22 @@ int CFlowGenList::load_client_config_file(std::string file_name) {
return (0);
}
+void CFlowGenList::set_client_config_tuple_gen_info(CTupleGenYamlInfo * tg) {
+ m_client_config_info.set_tuple_gen_info(tg);
+}
+
+void CFlowGenList::get_client_cfg_ip_list(std::vector<ClientCfgCompactEntry *> &ret) {
+ m_client_config_info.get_entry_list(ret);
+}
+
+void CFlowGenList::set_client_config_resolved_macs(CManyIPInfo &pretest_result) {
+ m_client_config_info.set_resolved_macs(pretest_result);
+}
+
+void CFlowGenList::dump_client_config(FILE *fd) {
+ m_client_config_info.dump(fd);
+}
+
int CFlowGenList::load_from_yaml(std::string file_name,
uint32_t num_threads){
uint8_t idx;
@@ -5217,11 +5248,11 @@ void CErfIF::add_vlan(uint16_t vlan_id) {
m_raw->pkt_len += 4;
}
-void CErfIF::apply_client_config(const ClientCfg *cfg, pkt_dir_t dir) {
+void CErfIF::apply_client_config(const ClientCfgBase *cfg, pkt_dir_t dir) {
assert(cfg);
uint8_t *p = (uint8_t *)m_raw->raw;
- const ClientCfgDir &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder);
+ const ClientCfgDirBase &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder);
/* dst mac */
if (cfg_dir.has_dst_mac_addr()) {
diff --git a/src/bp_sim.h b/src/bp_sim.h
index cd0f6a13..5d7df59d 100755
--- a/src/bp_sim.h
+++ b/src/bp_sim.h
@@ -44,6 +44,7 @@ limitations under the License.
#include <common/bitMan.h>
#include <yaml-cpp/yaml.h>
#include "trex_defs.h"
+#include "utl_ip.h"
#include "os_time.h"
#include "pal_utl.h"
#include "rx_check_header.h"
@@ -72,14 +73,9 @@ class CGenNodePCAP;
#define FORCE_NO_INLINE __attribute__ ((noinline))
#define FORCE_INLINE __attribute__((always_inline))
-/* IP address, last 32-bits of IPv6 remaps IPv4 */
-typedef struct {
- uint16_t v6[6]; /* First 96-bits of IPv6 */
- uint32_t v4; /* Last 32-bits IPv6 overloads v4 */
-} ipaddr_t;
-
/* reserve both 0xFF and 0xFE , router will -1 FF */
#define TTL_RESERVE_DUPLICATE 0xff
+#define TOS_TTL_RESERVE_DUPLICATE 0x1
/*
* Length of string needed to hold the largest port (16-bit) address
@@ -174,38 +170,6 @@ typedef enum { VM_REPLACE_IP_OFFSET =0x12, /* fix ip at offset */
/* work only on x86 littel */
#define MY_B(b) (((int)b)&0xff)
-// Routine to create IPv4 address string
-inline int ip_to_str(uint32_t ip,char * str){
- uint32_t ipv4 = PKT_HTONL(ip);
- inet_ntop(AF_INET, (const char *)&ipv4, str, INET_ADDRSTRLEN);
- return(strlen(str));
-}
-
-inline std::string ip_to_str(uint32_t ip) {
- char tmp[INET_ADDRSTRLEN];
- ip_to_str(ip, tmp);
- return tmp;
-}
-
-// Routine to create IPv6 address string
-inline int ipv6_to_str(ipaddr_t *ip,char * str){
- int idx=0;
- uint16_t ipv6[8];
- for (uint8_t i=0; i<6; i++) {
- ipv6[i] = PKT_HTONS(ip->v6[i]);
- }
- uint32_t ipv4 = PKT_HTONL(ip->v4);
- ipv6[6] = ipv4 & 0xffff;
- ipv6[7] = ipv4 >> 16;
-
- str[idx++] = '[';
- inet_ntop(AF_INET6, (const char *)&ipv6, &str[1], INET6_ADDRSTRLEN);
- idx = strlen(str);
- str[idx++] = ']';
- str[idx] = 0;
- return(idx);
-}
-
class CFlowPktInfo ;
class CMiniVM {
@@ -679,7 +643,6 @@ public:
CMacAddrCfg (){
memset(u.m_data,0,sizeof(u.m_data));
u.m_mac.dest[3]=1;
- u.m_mac.src[3]=1;
}
union {
mac_align_t m_mac;
@@ -693,21 +656,19 @@ class CPerPortIPCfg {
uint32_t get_mask() {return m_mask;}
uint32_t get_def_gw() {return m_def_gw;}
uint32_t get_vlan() {return m_vlan;}
- bool grat_arp_needed() {return m_grat_arp_needed;}
void set_ip(uint32_t val) {m_ip = val;}
void set_mask(uint32_t val) {m_mask = val;}
void set_def_gw(uint32_t val) {m_def_gw = val;}
void set_vlan(uint16_t val) {m_vlan = val;}
- void set_grat_arp_needed(bool val) {m_grat_arp_needed = val;}
private:
uint32_t m_def_gw;
uint32_t m_ip;
uint32_t m_mask;
uint16_t m_vlan;
- bool m_grat_arp_needed;
};
+
class CParserOption {
public:
@@ -824,6 +785,11 @@ public:
return ( (m_expected_portd>>1) * preview.getCores());
}
bool is_stateless(){
+ if (m_run_mode == RUN_MODE_INVALID) {
+ fprintf(stderr, "Internal bug: Calling is stateless before initializing run mode\n");
+ fprintf(stderr, "Try to put -i or -f <file> option as first in the option list\n");
+ exit(-1);
+ }
return (m_run_mode == RUN_MODE_INTERACTIVE ?true:false);
}
bool is_latency_enabled() {
@@ -1199,12 +1165,13 @@ public:
/* for simulation */
static void free_pools();
-
static inline rte_mbuf_t * pktmbuf_alloc_small(socket_id_t socket){
return ( m_mem_pool[socket].pktmbuf_alloc_small() );
}
-
+ static inline rte_mbuf_t * pktmbuf_alloc_small_by_port(uint8_t port_id) {
+ return ( m_mem_pool[m_socket.port_to_socket(port_id)].pktmbuf_alloc_small() );
+ }
/**
* try to allocate small buffers too
@@ -1222,6 +1189,13 @@ public:
return (m_mem_pool[socket].pktmbuf_alloc(size));
}
+ static inline rte_mbuf_t * pktmbuf_alloc_by_port(uint8_t port_id, uint16_t size){
+ socket_id_t socket = m_socket.port_to_socket(port_id);
+ if (size<FIRST_PKT_SIZE) {
+ return ( pktmbuf_alloc_small(socket));
+ }
+ return (m_mem_pool[socket].pktmbuf_alloc(size));
+ }
static inline bool is_learn_verify_mode(){
return ( (m_options.m_learn_mode != CParserOption::LEARN_MODE_DISABLED) && m_options.preview.get_learn_and_verify_mode_enable());
@@ -1550,7 +1524,7 @@ public:
uint32_t m_nat_tcp_seq_diff_server; // And some do seq num randomization for server->client also
uint16_t m_nat_external_port; // NAT client port
uint16_t m_nat_pad[1];
- const ClientCfg *m_client_cfg;
+ const ClientCfgBase *m_client_cfg;
uint32_t m_src_idx;
uint32_t m_dest_idx;
uint32_t m_end_of_cache_line[6];
@@ -1875,7 +1849,7 @@ typedef std::priority_queue<CGenNode *, std::vector<CGenNode *>,CGenNodeCompare>
class CErfIF : public CVirtualIF {
-
+ friend class basic_client_cfg_test1_Test;
public:
CErfIF(){
m_writer=NULL;
@@ -1914,7 +1888,7 @@ public:
protected:
void add_vlan(uint16_t vlan_id);
- void apply_client_config(const ClientCfg *cfg, pkt_dir_t dir);
+ void apply_client_config(const ClientCfgBase *cfg, pkt_dir_t dir);
virtual void fill_raw_packet(rte_mbuf_t * m,CGenNode * node,pkt_dir_t dir);
CFileWriterBase * m_writer;
@@ -2682,6 +2656,26 @@ public:
return (0);
}
}
+
+
+ void setTOSReserve(){
+ BP_ASSERT(l3.m_ipv4);
+ if (is_ipv6()) {
+ l3.m_ipv6->setTrafficClass(l3.m_ipv6->getTrafficClass() | TOS_TTL_RESERVE_DUPLICATE );
+ }else{
+ l3.m_ipv4->setTOS(l3.m_ipv4->getTOS()| TOS_TTL_RESERVE_DUPLICATE );
+ }
+ }
+
+ void clearTOSReserve(){
+ BP_ASSERT(l3.m_ipv4);
+ if (is_ipv6()) {
+ l3.m_ipv6->setTrafficClass(l3.m_ipv6->getTrafficClass()& (~TOS_TTL_RESERVE_DUPLICATE) );
+ }else{
+ l3.m_ipv4->setTOS(l3.m_ipv4->getTOS() & (~TOS_TTL_RESERVE_DUPLICATE) );
+ }
+ }
+
uint8_t getTTL(){
BP_ASSERT(l3.m_ipv4);
if (is_ipv6()) {
@@ -3054,6 +3048,8 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
printf(" %.3f : DP : learn packet !\n",now_sec());
#endif
ipv4->setTimeToLive(TTL_RESERVE_DUPLICATE);
+ ipv4->setTOS(ipv4->getTOS()|TOS_TTL_RESERVE_DUPLICATE);
+
/* first ipv4 option add the info in case of learn packet, usualy only the first packet */
if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_IP_OPTION)) {
@@ -3995,6 +3991,10 @@ public:
int load_from_yaml(std::string csv_file,uint32_t num_threads);
int load_client_config_file(std::string file_name);
+ void set_client_config_tuple_gen_info(CTupleGenYamlInfo * tg);
+ void get_client_cfg_ip_list(std::vector<ClientCfgCompactEntry *> &ret);
+ void set_client_config_resolved_macs(CManyIPInfo &pretest_result);
+ void dump_client_config(FILE *fd);
public:
void Dump(FILE *fd);
@@ -4240,6 +4240,33 @@ inline pkt_dir_t CGenNode::cur_interface_dir(){
}
}
+/* Itay: move this to a better place (common for RX STL and RX STF) */
+class CRXCoreIgnoreStat {
+ friend class CCPortLatency;
+ friend class CLatencyManager;
+ friend class RXGratARP;
+ public:
+ inline CRXCoreIgnoreStat operator- (const CRXCoreIgnoreStat &t_in) const {
+ CRXCoreIgnoreStat t_out;
+ t_out.m_tx_arp = this->m_tx_arp - t_in.m_tx_arp;
+ t_out.m_tx_ipv6_n_solic = this->m_tx_ipv6_n_solic - t_in.m_tx_ipv6_n_solic;
+ t_out.m_tot_bytes = this->m_tot_bytes - t_in.m_tot_bytes;
+ return t_out;
+ }
+ uint64_t get_tx_bytes() {return m_tot_bytes;}
+ uint64_t get_tx_pkts() {return m_tx_arp + m_tx_ipv6_n_solic;}
+ uint64_t get_tx_arp() {return m_tx_arp;}
+ uint64_t get_tx_n_solic() {return m_tx_ipv6_n_solic;}
+ void clear() {
+ m_tx_arp = 0;
+ m_tx_ipv6_n_solic = 0;
+ m_tot_bytes = 0;
+ }
+ private:
+ uint64_t m_tx_arp;
+ uint64_t m_tx_ipv6_n_solic;
+ uint64_t m_tot_bytes;
+};
#endif
diff --git a/src/common/Network/Packet/Arp.h b/src/common/Network/Packet/Arp.h
index a16605bd..e23ff139 100644
--- a/src/common/Network/Packet/Arp.h
+++ b/src/common/Network/Packet/Arp.h
@@ -20,8 +20,7 @@ limitations under the License.
#pragma pack(push, 1)
class ArpHdr {
public:
- enum arp_hdr_enum_e {
- ARP_HDR_HRD_ETHER = 1,
+ enum arp_hdr_op_e {
ARP_HDR_OP_REQUEST = 1, /* request to resolve address */
ARP_HDR_OP_REPLY = 2, /* response to previous request */
ARP_HDR_OP_REVREQUEST = 3, /* request proto addr given hardware */
@@ -30,6 +29,46 @@ class ArpHdr {
ARP_HDR_OP_INVREPLY = 6, /* response identifying peer */
};
+ enum arp_hdr_hrd_e {
+ ARP_HDR_HRD_ETHER = 1,
+ };
+
+ enum arp_hdr_proto_e {
+ ARP_HDR_PROTO_IPV4 = 0x800,
+ };
+
+ void setOp(uint16_t op) {
+ m_arp_op = PKT_HTONS(op);
+ }
+
+ uint16_t getOp() const {
+ return PKT_NTOHS(m_arp_op);
+ }
+
+ uint16_t getHrdType() const {
+ return PKT_NTOHS(m_arp_hrd);
+ }
+
+ uint16_t getProtocolType() const {
+ return PKT_NTOHS(m_arp_pro);
+ }
+
+ uint32_t getSip() const {
+ return PKT_NTOHL(m_arp_sip);
+ }
+
+ void setSip(uint32_t sip) {
+ m_arp_sip = PKT_HTONL(sip);
+ }
+
+ uint32_t getTip() const {
+ return PKT_NTOHL(m_arp_tip);
+ }
+
+ void setTip(uint32_t tip) {
+ m_arp_tip = PKT_HTONL(tip);
+ }
+
public:
uint16_t m_arp_hrd; /* format of hardware address */
uint16_t m_arp_pro; /* format of protocol address */
diff --git a/src/common/Network/Packet/EthernetHeader.h b/src/common/Network/Packet/EthernetHeader.h
index c9dcdbe2..002d6c25 100755
--- a/src/common/Network/Packet/EthernetHeader.h
+++ b/src/common/Network/Packet/EthernetHeader.h
@@ -62,8 +62,10 @@ public:
inline EthernetHeader(uint8_t* packet);
inline uint8_t* getPointer (){return (uint8_t*)this;}
- static inline uint32_t getSize (){return (uint32_t)sizeof(EthernetHeader);}
-
+ inline uint32_t getSize () {
+ return ( (getNextProtocol() == Protocol::VLAN) ? 18 : 14);
+ }
+
// Get dest MAC pointer
MacAddress *getDestMacP() { return &myDestination; }
diff --git a/src/common/Network/Packet/IPHeader.h b/src/common/Network/Packet/IPHeader.h
index da9ba52c..0bf97fbb 100755
--- a/src/common/Network/Packet/IPHeader.h
+++ b/src/common/Network/Packet/IPHeader.h
@@ -91,6 +91,15 @@ public:
*/
inline void setHeaderLength (uint8_t);
+ inline uint16_t getFirstWord(){
+ return PKT_NTOHS(*((uint16_t *)&myVer_HeaderLength));
+ }
+
+ inline void setFirstWord (uint16_t word){
+ *((uint16_t *)&myVer_HeaderLength) = PKT_NTOHS(word);
+ }
+
+
inline uint8_t getTOS ();
inline void setTOS (uint8_t);
diff --git a/src/common/Network/Packet/IcmpHeader.h b/src/common/Network/Packet/IcmpHeader.h
index 99d89329..4bc102e3 100644
--- a/src/common/Network/Packet/IcmpHeader.h
+++ b/src/common/Network/Packet/IcmpHeader.h
@@ -24,6 +24,11 @@ class ICMPHeader
{
public:
+ enum {
+ TYPE_ECHO_REPLY = 0,
+ TYPE_ECHO_REQUEST = 8,
+ };
+
ICMPHeader()
{
setCode(0);
diff --git a/src/common/Network/Packet/MacAddress.h b/src/common/Network/Packet/MacAddress.h
index 7e872fd6..924f774e 100755
--- a/src/common/Network/Packet/MacAddress.h
+++ b/src/common/Network/Packet/MacAddress.h
@@ -29,7 +29,7 @@ public:
MacAddress()
{
- set(0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef);
+ set(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
};
MacAddress(uint8_t a0,
@@ -47,15 +47,15 @@ public:
a5);
};
- MacAddress(uint8_t macAddr[ETHER_ADDR_LEN])
- {
- set(macAddr[0],
- macAddr[1],
- macAddr[2],
- macAddr[3],
- macAddr[4],
- macAddr[5] );
- };
+ MacAddress(const uint8_t macAddr[ETHER_ADDR_LEN])
+ {
+ set(macAddr[0],
+ macAddr[1],
+ macAddr[2],
+ macAddr[3],
+ macAddr[4],
+ macAddr[5] );
+ };
void set(uint8_t a0,
uint8_t a1,
@@ -81,50 +81,83 @@ public:
data[5]=val;
}
+ void set(uint64_t val) {
+ for (int i = 0; i < 6; i++) {
+ data[i] = ( val >> ((5 - i) * 8) ) & 0xFF;
+ }
+ }
+
+ bool isDefaultAddress() const
+ {
+ static MacAddress defaultMac;
+ return (*this == defaultMac);
+ }
+
+ bool isInvalidAddress() const
+ {
+ static MacAddress allZeros(0,0,0,0,0,0);
+ static MacAddress defaultMac;
+ return (*this == allZeros || *this == defaultMac);
+ }
+ void setIdentifierAsBogusAddr(uint32_t identifier)
+ {
+ *(uint32_t*)data = identifier;
+ }
+
+ uint32_t getIdentifierFromBogusAddr()
+ {
+ return *(uint32_t*)data;
+ }
+
+ MacAddress& operator+=(const int& val) {
+ uint64_t tmp = 0;
+
+ for (int i = 0; i < 6; i++) {
+ tmp <<= 8;
+ tmp |= data[i];
+ }
+
+ tmp += val;
+
+ for (int i = 0; i < 6; i++) {
+ data[i] = ( tmp >> ((5 - i) * 8) ) & 0xFF;
+ }
+
+ return *this;
+ }
+
+ bool operator == (const MacAddress& rhs) const
+ {
+ for(int i = 0; i < ETHER_ADDR_LEN; i++)
+ {
+ if(data[i] != rhs.data[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ MacAddress& operator = (const uint8_t *rhs) {
+ memcpy(data, rhs, ETHER_ADDR_LEN);
+ return (*this);
+ }
+
+ uint8_t* GetBuffer()
+ {
+ return data;
+ }
- bool isInvalidAddress() const
- {
- static MacAddress allZeros(0,0,0,0,0,0);
- static MacAddress cafeDeadBeef;
- return (*this == allZeros || *this == cafeDeadBeef);
- }
- void setIdentifierAsBogusAddr(uint32_t identifier)
- {
- *(uint32_t*)data = identifier;
- }
-
- uint32_t getIdentifierFromBogusAddr()
- {
- return *(uint32_t*)data;
- }
-
- bool operator == (const MacAddress& rhs) const
- {
- for(int i = 0; i < ETHER_ADDR_LEN; i++)
- {
- if(data[i] != rhs.data[i])
- return false;
- }
-
- return true;
- }
-
- uint8_t* GetBuffer()
- {
- return data;
- }
-
- const uint8_t* GetConstBuffer() const
- {
- return data;
- }
+ const uint8_t* GetConstBuffer() const
+ {
+ return data;
+ }
void dump(FILE *fd) const;
- void copyToArray(uint8_t *arrayToFill) const
- {
+ void copyToArray(uint8_t *arrayToFill) const
+ {
((uint32_t*)arrayToFill)[0] = ((uint32_t*)data)[0];//Copy first 32bit
- ((uint16_t*)arrayToFill)[2] = ((uint16_t*)data)[2];//Copy last 16bit
- }
+ ((uint16_t*)arrayToFill)[2] = ((uint16_t*)data)[2];//Copy last 16bit
+ }
public:
uint8_t data[ETHER_ADDR_LEN];
diff --git a/src/common/basic_utils.cpp b/src/common/basic_utils.cpp
index f169c29f..fded49ec 100755
--- a/src/common/basic_utils.cpp
+++ b/src/common/basic_utils.cpp
@@ -20,6 +20,10 @@ limitations under the License.
#include <sstream>
#include <sys/resource.h>
+#include "pal_utl.h"
+
+int my_inet_pton4(const char *src, unsigned char *dst);
+
bool utl_is_file_exists (const std::string& name) {
if (FILE *file = fopen(name.c_str(), "r")) {
fclose(file);
@@ -190,6 +194,26 @@ void utl_macaddr_to_str(const uint8_t *macaddr, std::string &output) {
}
+std::string utl_macaddr_to_str(const uint8_t *macaddr) {
+ std::string tmp;
+ utl_macaddr_to_str(macaddr, tmp);
+
+ return tmp;
+}
+
+bool utl_str_to_macaddr(const std::string &s, uint8_t *mac) {
+ int last = -1;
+ int rc = sscanf(s.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%n",
+ mac + 0, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5,
+ &last);
+
+ if ( (rc != 6) || (s.size() != last) ) {
+ return false;
+ }
+
+ return true;
+}
+
/**
* generate a random connection handler
*
@@ -248,3 +272,23 @@ void utl_set_coredump_size(long size, bool map_huge_pages) {
fprintf(fp, "%08x\n", mask);
fclose(fp);
}
+
+bool utl_ipv4_to_uint32(const char *ipv4_str, uint32_t &ipv4_num) {
+
+ uint32_t tmp;
+
+ int rc = my_inet_pton4(ipv4_str, (unsigned char *)&tmp);
+ if (!rc) {
+ return false;
+ }
+
+ ipv4_num = PAL_NTOHL(tmp);
+
+ return true;
+}
+
+std::string utl_uint32_to_ipv4(uint32_t ipv4_addr) {
+ std::stringstream ss;
+ ss << ((ipv4_addr >> 24) & 0xff) << "." << ((ipv4_addr >> 16) & 0xff) << "." << ((ipv4_addr >> 8) & 0xff) << "." << (ipv4_addr & 0xff);
+ return ss.str();
+}
diff --git a/src/common/basic_utils.h b/src/common/basic_utils.h
index f6250a2b..36f9db85 100755
--- a/src/common/basic_utils.h
+++ b/src/common/basic_utils.h
@@ -85,6 +85,9 @@ inline void utl_swap(T& a, T& b) {
bool utl_is_file_exists (const std::string& name) ;
void utl_macaddr_to_str(const uint8_t *macaddr, std::string &output);
+std::string utl_macaddr_to_str(const uint8_t *macaddr);
+
+bool utl_str_to_macaddr(const std::string &s, uint8_t *mac);
std::string utl_generate_random_str(unsigned int &seed, int len);
@@ -98,6 +101,9 @@ std::string utl_generate_random_str(unsigned int &seed, int len);
*/
void utl_set_coredump_size(long size, bool map_huge_pages = false);
+bool utl_ipv4_to_uint32(const char *ipv4_str, uint32_t &ipv4_num);
+std::string utl_uint32_to_ipv4(uint32_t ipv4_addr);
+
#endif
diff --git a/src/common/captureFile.h b/src/common/captureFile.h
index fefa62bd..d87e57b6 100755
--- a/src/common/captureFile.h
+++ b/src/common/captureFile.h
@@ -227,9 +227,10 @@ class CFileWriterBase {
public:
- virtual ~CFileWriterBase(){};
- virtual bool Create(char * name) = 0;
+ virtual ~CFileWriterBase(){};
+ virtual bool Create(char * name) = 0;
virtual bool write_packet(CCapPktRaw * lpPacket)=0;
+ virtual void flush_to_disk() = 0;
};
diff --git a/src/common/erf.cpp b/src/common/erf.cpp
index 76945b01..f872a281 100755
--- a/src/common/erf.cpp
+++ b/src/common/erf.cpp
@@ -280,7 +280,11 @@ bool CErfFileWriter::write_packet(CCapPktRaw * lpPacket){
return true;
}
-
+void CErfFileWriter::flush_to_disk() {
+ if (m_fd) {
+ fflush(m_fd);
+ }
+}
bool CPcapFileWriter::Create(char *file_name){
m_fd=CAP_FOPEN_64(file_name,"wb");
diff --git a/src/common/erf.h b/src/common/erf.h
index e1b83e46..bec94872 100755
--- a/src/common/erf.h
+++ b/src/common/erf.h
@@ -224,6 +224,13 @@ public:
virtual bool Create(char *file_name);
void Delete();
virtual bool write_packet(CCapPktRaw * lpPacket);
+
+ /**
+ * flush all packets to disk
+ *
+ */
+ void flush_to_disk();
+
private:
FILE *m_fd;
int m_cnt;
diff --git a/src/common/pcap.cpp b/src/common/pcap.cpp
index 8e9bf0ac..b976aed7 100755
--- a/src/common/pcap.cpp
+++ b/src/common/pcap.cpp
@@ -223,11 +223,18 @@ bool LibPCapWriter::Create(char * name)
printf(" ERROR create file \n");
return(false);
}
+
/* prepare the write counter */
m_pkt_count = 0;
return init();
}
+void LibPCapWriter::flush_to_disk() {
+ if (m_is_open) {
+ fflush(m_file_handler);
+ }
+}
+
/**
*
* Write the libpcap header.
diff --git a/src/common/pcap.h b/src/common/pcap.h
index 3f8dfd21..265ea17b 100755
--- a/src/common/pcap.h
+++ b/src/common/pcap.h
@@ -1,5 +1,5 @@
-#ifndef __LIBPCAP_H__
-#define __LIBPCAP_H__
+#ifndef __TREX_LIBPCAP_H__
+#define __TREX_LIBPCAP_H__
/*
Copyright (c) 2015-2015 Cisco Systems, Inc.
@@ -143,6 +143,13 @@ public:
*/
void Close();
+ /**
+ * flush all packets to disk
+ *
+ * @author imarom (11/24/2016)
+ */
+ void flush_to_disk();
+
private:
bool init();
@@ -151,4 +158,5 @@ private:
bool m_is_open;
uint32_t m_pkt_count;
};
+
#endif
diff --git a/src/debug.cpp b/src/debug.cpp
index 3a9cd506..e272424c 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -106,7 +106,7 @@ rte_mbuf_t *CTrexDebug::create_test_pkt(int ip_ver, uint16_t l4_proto, uint8_t t
0x07, 0x08, 0x50, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0a, 0x01, 0x02, 0x03, 0x04,
// bad - 0x03, 0x04, 0x06, 0x02, 0x20, 0x00, 0xBB, 0x79, 0x00, 0x00};
0x03, 0x04, 0x50, 0x02, 0x20, 0x00, 0xBB, 0x79, 0x00, 0x00};
- rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc(0, sizeof(test_pkt));
+ rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc_by_port(0, sizeof(test_pkt));
char *p = rte_pktmbuf_append(m, sizeof(test_pkt));
assert(p);
@@ -141,7 +141,12 @@ rte_mbuf_t *CTrexDebug::create_test_pkt(int ip_ver, uint16_t l4_proto, uint8_t t
}
pkt = CTestPktGen::create_test_pkt(l3_type, l4_proto, ttl, ip_id, flags, 1000, pkt_size);
- m = CGlobalInfo::pktmbuf_alloc(0, pkt_size);
+
+ /* DEBUG print the packet
+ utl_k12_pkt_format(stdout,pkt, pkt_size) ;
+ */
+
+ m = CGlobalInfo::pktmbuf_alloc_by_port(0, pkt_size);
if ( unlikely(m == 0) ) {
printf("ERROR no packets \n");
return (NULL);
@@ -156,7 +161,7 @@ rte_mbuf_t *CTrexDebug::create_test_pkt(int ip_ver, uint16_t l4_proto, uint8_t t
#endif
rte_mbuf_t *CTrexDebug::create_pkt(uint8_t *pkt, int pkt_size) {
- rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc(0, pkt_size);
+ rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc_by_port(0, pkt_size);
if ( unlikely(m == 0) ) {
printf("ERROR no packets \n");
return 0;
@@ -170,7 +175,7 @@ rte_mbuf_t *CTrexDebug::create_pkt(uint8_t *pkt, int pkt_size) {
}
rte_mbuf_t *CTrexDebug::create_pkt_indirect(rte_mbuf_t *m, uint32_t new_pkt_size){
- rte_mbuf_t *d = CGlobalInfo::pktmbuf_alloc(0, 60);
+ rte_mbuf_t *d = CGlobalInfo::pktmbuf_alloc_by_port(0, 60);
assert(d);
rte_pktmbuf_attach(d, m);
@@ -341,7 +346,7 @@ int CTrexDebug::verify_hw_rules(bool recv_all) {
rte_mbuf_t *m = NULL;
CPhyEthIF * lp;
rte_mbuf_t * rx_pkts[32];
- int sent_num = 20;
+ int sent_num = 8; /* reduce the size, there are driver that can handle only burst of 8 in QUEUE 0 */
int ret = 0;
for (int pkt_num = 0; pkt_num < sizeof(test_pkts) / sizeof (test_pkts[0]); pkt_num++) {
@@ -365,6 +370,7 @@ int CTrexDebug::verify_hw_rules(bool recv_all) {
case STL:
if ( CGlobalInfo::m_options.is_stateless() ) {
exp_q = MAIN_DPDK_RX_Q;
+ pkt_flags |= DPF_TOS_1;
} else {
exp_q = MAIN_DPDK_DATA_Q;
}
@@ -374,6 +380,7 @@ int CTrexDebug::verify_hw_rules(bool recv_all) {
exp_q = MAIN_DPDK_DATA_Q;
} else {
exp_q = MAIN_DPDK_RX_Q;
+ pkt_flags |= DPF_TOS_1;
}
break;
default:
diff --git a/src/dpdk/drivers/net/enic/base/vnic_dev.c b/src/dpdk/drivers/net/enic/base/vnic_dev.c
index fc2e4cc3..e50b90e7 100644
--- a/src/dpdk/drivers/net/enic/base/vnic_dev.c
+++ b/src/dpdk/drivers/net/enic/base/vnic_dev.c
@@ -462,6 +462,18 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
}
}
+int vnic_dev_capable_adv_filters(struct vnic_dev *vdev)
+{
+ u64 a0 = (u32)CMD_ADD_ADV_FILTER, a1 = 0;
+ int wait = 1000;
+ int err;
+
+ err = vnic_dev_cmd(vdev, CMD_CAPABILITY, &a0, &a1, wait);
+ if (err)
+ return 0;
+ return (a1 >= (u32)FILTER_DPDK_1);
+}
+
static int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd)
{
u64 a0 = (u32)cmd, a1 = 0;
@@ -655,7 +667,12 @@ int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
(promisc ? CMD_PFILTER_PROMISCUOUS : 0) |
(allmulti ? CMD_PFILTER_ALL_MULTICAST : 0);
+#define TREX_PATCH
+#ifdef TREX_PATCH
+ err = vnic_dev_cmd(vdev, CMD_PACKET_FILTER_ALL, &a0, &a1, wait);
+#else
err = vnic_dev_cmd(vdev, CMD_PACKET_FILTER, &a0, &a1, wait);
+#endif
if (err)
pr_err("Can't set packet filter\n");
@@ -999,7 +1016,7 @@ int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr)
* @data: filter data
*/
int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
- struct filter *data)
+ struct filter_v2 *data)
{
u64 a0, a1;
int wait = 1000;
@@ -1008,11 +1025,20 @@ int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
struct filter_tlv *tlv, *tlv_va;
struct filter_action *action;
u64 tlv_size;
+ u32 filter_size;
static unsigned int unique_id;
char z_name[RTE_MEMZONE_NAMESIZE];
+ enum vnic_devcmd_cmd dev_cmd;
+
if (cmd == CLSF_ADD) {
- tlv_size = sizeof(struct filter) +
+ if (data->type == FILTER_DPDK_1)
+ dev_cmd = CMD_ADD_ADV_FILTER;
+ else
+ dev_cmd = CMD_ADD_FILTER;
+
+ filter_size = vnic_filter_size(data);
+ tlv_size = filter_size +
sizeof(struct filter_action) +
2*sizeof(struct filter_tlv);
snprintf((char *)z_name, sizeof(z_name),
@@ -1026,12 +1052,12 @@ int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
a1 = tlv_size;
memset(tlv, 0, tlv_size);
tlv->type = CLSF_TLV_FILTER;
- tlv->length = sizeof(struct filter);
- *(struct filter *)&tlv->val = *data;
+ tlv->length = filter_size;
+ memcpy(&tlv->val, (void *)data, filter_size);
tlv = (struct filter_tlv *)((char *)tlv +
sizeof(struct filter_tlv) +
- sizeof(struct filter));
+ filter_size);
tlv->type = CLSF_TLV_ACTION;
tlv->length = sizeof(struct filter_action);
@@ -1039,7 +1065,7 @@ int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
action->type = FILTER_ACTION_RQ_STEERING;
action->u.rq_idx = *entry;
- ret = vnic_dev_cmd(vdev, CMD_ADD_FILTER, &a0, &a1, wait);
+ ret = vnic_dev_cmd(vdev, dev_cmd, &a0, &a1, wait);
*entry = (u16)a0;
vdev->free_consistent(vdev->priv, tlv_size, tlv_va, tlv_pa);
} else if (cmd == CLSF_DEL) {
diff --git a/src/dpdk/drivers/net/enic/base/vnic_dev.h b/src/dpdk/drivers/net/enic/base/vnic_dev.h
index 689442f3..06ebd4ce 100644
--- a/src/dpdk/drivers/net/enic/base/vnic_dev.h
+++ b/src/dpdk/drivers/net/enic/base/vnic_dev.h
@@ -134,6 +134,7 @@ void vnic_dev_cmd_proxy_by_bdf_start(struct vnic_dev *vdev, u16 bdf);
void vnic_dev_cmd_proxy_end(struct vnic_dev *vdev);
int vnic_dev_fw_info(struct vnic_dev *vdev,
struct vnic_devcmd_fw_info **fw_info);
+int vnic_dev_capable_adv_filters(struct vnic_dev *vdev);
int vnic_dev_asic_info(struct vnic_dev *vdev, u16 *asic_type, u16 *asic_rev);
int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, size_t size,
void *value);
@@ -201,7 +202,7 @@ int vnic_dev_enable2_done(struct vnic_dev *vdev, int *status);
int vnic_dev_deinit_done(struct vnic_dev *vdev, int *status);
int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr);
int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
- struct filter *data);
+ struct filter_v2 *data);
#ifdef ENIC_VXLAN
int vnic_dev_overlay_offload_enable_disable(struct vnic_dev *vdev,
u8 overlay, u8 config);
diff --git a/src/dpdk/drivers/net/enic/base/vnic_devcmd.h b/src/dpdk/drivers/net/enic/base/vnic_devcmd.h
index b3d5a6cc..785fd6fd 100644
--- a/src/dpdk/drivers/net/enic/base/vnic_devcmd.h
+++ b/src/dpdk/drivers/net/enic/base/vnic_devcmd.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved.
+ * Copyright 2008-2016 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
*
* Copyright (c) 2014, Cisco Systems, Inc.
@@ -126,7 +126,8 @@ enum vnic_devcmd_cmd {
/* dev-specific block member:
* in: (u16)a0=offset,(u8)a1=size
- * out: a0=value */
+ * out: a0=value
+ */
CMD_DEV_SPEC = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 2),
/* stats clear */
@@ -146,8 +147,9 @@ enum vnic_devcmd_cmd {
CMD_HANG_NOTIFY = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 8),
/* MAC address in (u48)a0 */
- CMD_GET_MAC_ADDR = _CMDC(_CMD_DIR_READ,
+ CMD_MAC_ADDR = _CMDC(_CMD_DIR_READ,
_CMD_VTYPE_ENET | _CMD_VTYPE_FC, 9),
+#define CMD_GET_MAC_ADDR CMD_MAC_ADDR /* some uses are aliased */
/* add addr from (u48)a0 */
CMD_ADDR_ADD = _CMDCNW(_CMD_DIR_WRITE,
@@ -387,9 +389,8 @@ enum vnic_devcmd_cmd {
* Subvnic migration from MQ <--> VF.
* Enable the LIF migration from MQ to VF and vice versa. MQ and VF
* indexes are statically bound at the time of initialization.
- * Based on the
- * direction of migration, the resources of either MQ or the VF shall
- * be attached to the LIF.
+ * Based on the direction of migration, the resources of either MQ or
+ * the VF shall be attached to the LIF.
* in: (u32)a0=Direction of Migration
* 0=> Migrate to VF
* 1=> Migrate to MQ
@@ -397,7 +398,6 @@ enum vnic_devcmd_cmd {
*/
CMD_MIGRATE_SUBVNIC = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 53),
-
/*
* Register / Deregister the notification block for MQ subvnics
* in:
@@ -433,6 +433,10 @@ enum vnic_devcmd_cmd {
* in: (u64) a0= filter address
* (u32) a1= size of filter
* out: (u32) a0=filter identifier
+ *
+ * Capability query:
+ * out: (u64) a0= 1 if capability query supported
+ * (u64) a1= MAX filter type supported
*/
CMD_ADD_FILTER = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ENET, 58),
@@ -471,23 +475,133 @@ enum vnic_devcmd_cmd {
CMD_QP_STATS_CLEAR = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 63),
/*
- * Enable/Disable overlay offloads on the given vnic
+ * UEFI BOOT API: (u64)a0= UEFI FLS_CMD_xxx
+ * (ui64)a1= paddr for the info buffer
+ */
+ CMD_FC_REQ = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_FC, 64),
+
+ /*
+ * Return the iSCSI config details required by the EFI Option ROM
+ * in: (u32) a0=0 Get Boot Info for PXE eNIC as per pxe_boot_config_t
+ * a0=1 Get Boot info for iSCSI enic as per
+ * iscsi_boot_efi_cfg_t
+ * in: (u64) a1=Host address where iSCSI config info is returned
+ */
+ CMD_VNIC_BOOT_CONFIG_INFO = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 65),
+
+ /*
+ * Create a Queue Pair (RoCE)
+ * in: (u32) a0 = Queue Pair number
+ * (u32) a1 = Remote QP
+ * (u32) a2 = RDMA-RQ
+ * (u16) a3 = RQ Res Group
+ * (u16) a4 = SQ Res Group
+ * (u32) a5 = Protection Domain
+ * (u64) a6 = Remote MAC
+ * (u32) a7 = start PSN
+ * (u16) a8 = MSS
+ * (u32) a9 = protocol version
+ */
+ CMD_RDMA_QP_CREATE = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 66),
+
+ /*
+ * Delete a Queue Pair (RoCE)
+ * in: (u32) a0 = Queue Pair number
+ */
+ CMD_RDMA_QP_DELETE = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 67),
+
+ /*
+ * Retrieve a Queue Pair's status information (RoCE)
+ * in: (u32) a0 = Queue Pair number
+ * (u64) a1 = host buffer addr for QP status struct
+ * (u32) a2 = length of the buffer
+ */
+ CMD_RDMA_QP_STATUS = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ENET, 68),
+
+ /*
+ * Use this devcmd for agreeing on the highest common version supported
+ * by both driver and fw for by features who need such a facility.
+ * in: (u64) a0 = feature (driver requests for the supported versions
+ * on this feature)
+ * out: (u64) a0 = bitmap of all supported versions for that feature
+ */
+ CMD_GET_SUPP_FEATURE_VER = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ENET, 69),
+
+ /*
+ * Initialize the RDMA notification work queue
+ * in: (u64) a0 = host buffer address
+ * in: (u16) a1 = number of entries in buffer
+ * in: (u16) a2 = resource group number
+ * in: (u16) a3 = CQ number to post completion
+ */
+ CMD_RDMA_INIT_INFO_BUF = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 70),
+
+ /*
+ * De-init the RDMA notification work queue
+ * in: (u64) a0=resource group number
+ */
+ CMD_RDMA_DEINIT_INFO_BUF = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 71),
+
+ /*
+ * Control (Enable/Disable) overlay offloads on the given vnic
* in: (u8) a0 = OVERLAY_FEATURE_NVGRE : NVGRE
* a0 = OVERLAY_FEATURE_VXLAN : VxLAN
- * in: (u8) a1 = OVERLAY_OFFLOAD_ENABLE : Enable
- * a1 = OVERLAY_OFFLOAD_DISABLE : Disable
+ * in: (u8) a1 = OVERLAY_OFFLOAD_ENABLE : Enable or
+ * a1 = OVERLAY_OFFLOAD_DISABLE : Disable or
+ * a1 = OVERLAY_OFFLOAD_ENABLE_V2 : Enable with version 2
*/
- CMD_OVERLAY_OFFLOAD_ENABLE_DISABLE =
- _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 72),
+ CMD_OVERLAY_OFFLOAD_CTRL =
+ _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 72),
/*
* Configuration of overlay offloads feature on a given vNIC
- * in: (u8) a0 = DEVCMD_OVERLAY_NVGRE : NVGRE
- * a0 = DEVCMD_OVERLAY_VXLAN : VxLAN
- * in: (u8) a1 = VXLAN_PORT_UPDATE : VxLAN
- * in: (u16) a2 = unsigned short int port information
+ * in: (u8) a0 = OVERLAY_CFG_VXLAN_PORT_UPDATE : VxLAN
+ * in: (u16) a1 = unsigned short int port information
*/
CMD_OVERLAY_OFFLOAD_CFG = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 73),
+
+ /*
+ * Return the configured name for the device
+ * in: (u64) a0=Host address where the name is copied
+ * (u32) a1=Size of the buffer
+ */
+ CMD_GET_CONFIG_NAME = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 74),
+
+ /*
+ * Enable group interrupt for the VF
+ * in: (u32) a0 = GRPINTR_ENABLE : enable
+ * a0 = GRPINTR_DISABLE : disable
+ * a0 = GRPINTR_UPD_VECT: update group vector addr
+ * in: (u32) a1 = interrupt group count
+ * in: (u64) a2 = Start of host buffer address for DMAing group
+ * vector bitmap
+ * in: (u64) a3 = Stride between group vectors
+ */
+ CMD_CONFIG_GRPINTR = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 75),
+
+ /*
+ * Set cq arrary base and size in a list of consective wqs and
+ * rqs for a device
+ * in: (u16) a0 = the wq relative index in the device.
+ * -1 indicates skipping wq configuration
+ * in: (u16) a1 = the wcq relative index in the device
+ * in: (u16) a2 = the rq relative index in the device
+ * -1 indicates skipping rq configuration
+ * in: (u16) a3 = the rcq relative index in the device
+ */
+ CMD_CONFIG_CQ_ARRAY = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 76),
+
+ /*
+ * Add an advanced filter.
+ * in: (u64) a0= filter address
+ * (u32) a1= size of filter
+ * out: (u32) a0=filter identifier
+ *
+ * Capability query:
+ * out: (u64) a0= 1 if capabliity query supported
+ * (u64) a1= MAX filter type supported
+ */
+ CMD_ADD_ADV_FILTER = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ENET, 77),
};
/* CMD_ENABLE2 flags */
@@ -520,6 +634,9 @@ enum vnic_devcmd_status {
STAT_NONE = 0,
STAT_BUSY = 1 << 0, /* cmd in progress */
STAT_ERROR = 1 << 1, /* last cmd caused error (code in a0) */
+ STAT_FAILOVER = 1 << 2, /* always set on vnics in pci standby state
+ * if seen a failover to the standby happened
+ */
};
enum vnic_devcmd_error {
@@ -558,9 +675,9 @@ enum fwinfo_asic_type {
FWINFO_ASIC_TYPE_UNKNOWN,
FWINFO_ASIC_TYPE_PALO,
FWINFO_ASIC_TYPE_SERENO,
+ FWINFO_ASIC_TYPE_CRUZ,
};
-
struct vnic_devcmd_notify {
u32 csum; /* checksum over following words */
@@ -595,25 +712,16 @@ struct vnic_devcmd_provinfo {
*/
#define FILTER_FIELD_VALID(fld) (1 << (fld - 1))
-#define FILTER_FIELDS_USNIC (FILTER_FIELD_VALID(1) | \
- FILTER_FIELD_VALID(2) | \
- FILTER_FIELD_VALID(3) | \
- FILTER_FIELD_VALID(4))
-
-#define FILTER_FIELDS_IPV4_5TUPLE (FILTER_FIELD_VALID(1) | \
- FILTER_FIELD_VALID(2) | \
- FILTER_FIELD_VALID(3) | \
- FILTER_FIELD_VALID(4) | \
- FILTER_FIELD_VALID(5))
-
-#define FILTER_FIELDS_MAC_VLAN (FILTER_FIELD_VALID(1) | \
- FILTER_FIELD_VALID(2))
-
#define FILTER_FIELD_USNIC_VLAN FILTER_FIELD_VALID(1)
#define FILTER_FIELD_USNIC_ETHTYPE FILTER_FIELD_VALID(2)
#define FILTER_FIELD_USNIC_PROTO FILTER_FIELD_VALID(3)
#define FILTER_FIELD_USNIC_ID FILTER_FIELD_VALID(4)
+#define FILTER_FIELDS_USNIC (FILTER_FIELD_USNIC_VLAN | \
+ FILTER_FIELD_USNIC_ETHTYPE | \
+ FILTER_FIELD_USNIC_PROTO | \
+ FILTER_FIELD_USNIC_ID)
+
struct filter_usnic_id {
u32 flags;
u16 vlan;
@@ -628,10 +736,18 @@ struct filter_usnic_id {
#define FILTER_FIELD_5TUP_SRC_PT FILTER_FIELD_VALID(4)
#define FILTER_FIELD_5TUP_DST_PT FILTER_FIELD_VALID(5)
+#define FILTER_FIELDS_IPV4_5TUPLE (FILTER_FIELD_5TUP_PROTO | \
+ FILTER_FIELD_5TUP_SRC_AD | \
+ FILTER_FIELD_5TUP_DST_AD | \
+ FILTER_FIELD_5TUP_SRC_PT | \
+ FILTER_FIELD_5TUP_DST_PT)
+
/* Enums for the protocol field. */
enum protocol_e {
PROTO_UDP = 0,
PROTO_TCP = 1,
+ PROTO_IPV4 = 2,
+ PROTO_IPV6 = 3
};
struct filter_ipv4_5tuple {
@@ -646,12 +762,78 @@ struct filter_ipv4_5tuple {
#define FILTER_FIELD_VMQ_VLAN FILTER_FIELD_VALID(1)
#define FILTER_FIELD_VMQ_MAC FILTER_FIELD_VALID(2)
+#define FILTER_FIELDS_MAC_VLAN (FILTER_FIELD_VMQ_VLAN | \
+ FILTER_FIELD_VMQ_MAC)
+
+#define FILTER_FIELDS_NVGRE FILTER_FIELD_VMQ_MAC
+
struct filter_mac_vlan {
u32 flags;
u16 vlan;
u8 mac_addr[6];
} __attribute__((packed));
+#define FILTER_FIELD_VLAN_IP_3TUP_VLAN FILTER_FIELD_VALID(1)
+#define FILTER_FIELD_VLAN_IP_3TUP_L3_PROTO FILTER_FIELD_VALID(2)
+#define FILTER_FIELD_VLAN_IP_3TUP_DST_AD FILTER_FIELD_VALID(3)
+#define FILTER_FIELD_VLAN_IP_3TUP_L4_PROTO FILTER_FIELD_VALID(4)
+#define FILTER_FIELD_VLAN_IP_3TUP_DST_PT FILTER_FIELD_VALID(5)
+
+#define FILTER_FIELDS_VLAN_IP_3TUP (FILTER_FIELD_VLAN_IP_3TUP_VLAN | \
+ FILTER_FIELD_VLAN_IP_3TUP_L3_PROTO | \
+ FILTER_FIELD_VLAN_IP_3TUP_DST_AD | \
+ FILTER_FIELD_VLAN_IP_3TUP_L4_PROTO | \
+ FILTER_FIELD_VLAN_IP_3TUP_DST_PT)
+
+struct filter_vlan_ip_3tuple {
+ u32 flags;
+ u16 vlan;
+ u16 l3_protocol;
+ union {
+ u32 dst_addr_v4;
+ u8 dst_addr_v6[16];
+ } u;
+ u32 l4_protocol;
+ u16 dst_port;
+} __attribute__((packed));
+
+#define FILTER_GENERIC_1_BYTES 64
+
+enum filter_generic_1_layer {
+ FILTER_GENERIC_1_L2,
+ FILTER_GENERIC_1_L3,
+ FILTER_GENERIC_1_L4,
+ FILTER_GENERIC_1_L5,
+ FILTER_GENERIC_1_NUM_LAYERS
+};
+
+#define FILTER_GENERIC_1_IPV4 (1 << 0)
+#define FILTER_GENERIC_1_IPV6 (1 << 1)
+#define FILTER_GENERIC_1_UDP (1 << 2)
+#define FILTER_GENERIC_1_TCP (1 << 3)
+#define FILTER_GENERIC_1_TCP_OR_UDP (1 << 4)
+#define FILTER_GENERIC_1_IP4SUM_OK (1 << 5)
+#define FILTER_GENERIC_1_L4SUM_OK (1 << 6)
+#define FILTER_GENERIC_1_IPFRAG (1 << 7)
+
+#define FILTER_GENERIC_1_KEY_LEN 64
+
+/*
+ * Version 1 of generic filter specification
+ * position is only 16 bits, reserving positions > 64k to be used by firmware
+ */
+struct filter_generic_1 {
+ u16 position; /* lower position comes first */
+ u32 mask_flags;
+ u32 val_flags;
+ u16 mask_vlan;
+ u16 val_vlan;
+ struct {
+ u8 mask[FILTER_GENERIC_1_KEY_LEN]; /* 0 bit means "don't care"*/
+ u8 val[FILTER_GENERIC_1_KEY_LEN];
+ } __attribute__((packed)) layer[FILTER_GENERIC_1_NUM_LAYERS];
+} __attribute__((packed));
+
/* Specifies the filter_action type. */
enum {
FILTER_ACTION_RQ_STEERING = 0,
@@ -670,6 +852,10 @@ enum filter_type {
FILTER_USNIC_ID = 0,
FILTER_IPV4_5TUPLE = 1,
FILTER_MAC_VLAN = 2,
+ FILTER_VLAN_IP_3TUPLE = 3,
+ FILTER_NVGRE_VMQ = 4,
+ FILTER_USNIC_IP = 5,
+ FILTER_DPDK_1 = 6,
FILTER_MAX
};
@@ -679,6 +865,27 @@ struct filter {
struct filter_usnic_id usnic;
struct filter_ipv4_5tuple ipv4;
struct filter_mac_vlan mac_vlan;
+ struct filter_vlan_ip_3tuple vlan_3tuple;
+ } u;
+} __attribute__((packed));
+
+/*
+ * This is a strict superset of "struct filter" and exists only
+ * because many drivers use "sizeof (struct filter)" in deciding TLV size.
+ * This new, larger struct filter would cause any code that uses that method
+ * to not work with older firmware, so we add filter_v2 to hold the
+ * new filter types. Drivers should use vnic_filter_size() to determine
+ * the TLV size instead of sizeof (struct fiter_v2) to guard against future
+ * growth.
+ */
+struct filter_v2 {
+ u32 type;
+ union {
+ struct filter_usnic_id usnic;
+ struct filter_ipv4_5tuple ipv4;
+ struct filter_mac_vlan mac_vlan;
+ struct filter_vlan_ip_3tuple vlan_3tuple;
+ struct filter_generic_1 generic_1;
} u;
} __attribute__((packed));
@@ -687,14 +894,55 @@ enum {
CLSF_TLV_ACTION = 1,
};
-#define FILTER_MAX_BUF_SIZE 100 /* Maximum size of buffer to CMD_ADD_FILTER */
-
struct filter_tlv {
- uint32_t type;
- uint32_t length;
- uint32_t val[0];
+ u_int32_t type;
+ u_int32_t length;
+ u_int32_t val[0];
};
+/* Data for CMD_ADD_FILTER is 2 TLV and filter + action structs */
+#define FILTER_MAX_BUF_SIZE 100
+#define FILTER_V2_MAX_BUF_SIZE (sizeof(struct filter_v2) + \
+ sizeof(struct filter_action) + \
+ (2 * sizeof(struct filter_tlv)))
+
+/*
+ * Compute actual structure size given filter type. To be "future-proof,"
+ * drivers should use this instead of "sizeof (struct filter_v2)" when
+ * computing length for TLV.
+ */
+static inline u_int32_t
+vnic_filter_size(struct filter_v2 *fp)
+{
+ u_int32_t size;
+
+ switch (fp->type) {
+ case FILTER_USNIC_ID:
+ size = sizeof(fp->u.usnic);
+ break;
+ case FILTER_IPV4_5TUPLE:
+ size = sizeof(fp->u.ipv4);
+ break;
+ case FILTER_MAC_VLAN:
+ case FILTER_NVGRE_VMQ:
+ size = sizeof(fp->u.mac_vlan);
+ break;
+ case FILTER_VLAN_IP_3TUPLE:
+ size = sizeof(fp->u.vlan_3tuple);
+ break;
+ case FILTER_USNIC_IP:
+ case FILTER_DPDK_1:
+ size = sizeof(fp->u.generic_1);
+ break;
+ default:
+ size = sizeof(fp->u);
+ break;
+ }
+ size += sizeof(fp->type);
+ return size;
+}
+
+
enum {
CLSF_ADD = 0,
CLSF_DEL = 1,
@@ -766,8 +1014,30 @@ typedef enum {
OVERLAY_FEATURE_MAX,
} overlay_feature_t;
-#define OVERLAY_OFFLOAD_ENABLE 0
-#define OVERLAY_OFFLOAD_DISABLE 1
+#define OVERLAY_OFFLOAD_ENABLE 0
+#define OVERLAY_OFFLOAD_DISABLE 1
+#define OVERLAY_OFFLOAD_ENABLE_V2 2
#define OVERLAY_CFG_VXLAN_PORT_UPDATE 0
+
+/*
+ * Use this enum to get the supported versions for each of these features
+ * If you need to use the devcmd_get_supported_feature_version(), add
+ * the new feature into this enum and install function handler in devcmd.c
+ */
+typedef enum {
+ VIC_FEATURE_VXLAN,
+ VIC_FEATURE_RDMA,
+ VIC_FEATURE_MAX,
+} vic_feature_t;
+
+/*
+ * CMD_CONFIG_GRPINTR subcommands
+ */
+typedef enum {
+ GRPINTR_ENABLE = 1,
+ GRPINTR_DISABLE,
+ GRPINTR_UPD_VECT,
+} grpintr_subcmd_t;
+
#endif /* _VNIC_DEVCMD_H_ */
diff --git a/src/dpdk/drivers/net/enic/enic.h b/src/dpdk/drivers/net/enic/enic.h
index 4c16ef17..c00d3fb8 100644
--- a/src/dpdk/drivers/net/enic/enic.h
+++ b/src/dpdk/drivers/net/enic/enic.h
@@ -92,6 +92,12 @@ struct enic_fdir {
struct rte_eth_fdir_stats stats;
struct rte_hash *hash;
struct enic_fdir_node *nodes[ENICPMD_FDIR_MAX];
+ u32 modes;
+ u32 types_mask;
+ void (*copy_fltr_fn)(struct filter_v2 *filt,
+ struct rte_eth_fdir_input *input,
+ struct rte_eth_fdir_masks *masks);
+
};
struct enic_soft_stats {
@@ -128,6 +134,7 @@ struct enic {
int link_status;
u8 hw_ip_checksum;
u16 max_mtu;
+ u16 adv_filters;
unsigned int flags;
unsigned int priv_flags;
@@ -273,4 +280,11 @@ uint16_t enic_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
uint16_t enic_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
uint16_t nb_pkts);
int enic_set_mtu(struct enic *enic, uint16_t new_mtu);
+void enic_fdir_info(struct enic *enic);
+void enic_fdir_info_get(struct enic *enic, struct rte_eth_fdir_info *stats);
+void copy_fltr_v1(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
+ struct rte_eth_fdir_masks *masks);
+void copy_fltr_v2(__rte_unused struct filter_v2 *fltr,
+ __rte_unused struct rte_eth_fdir_input *input,
+ __rte_unused struct rte_eth_fdir_masks *masks);
#endif /* _ENIC_H_ */
diff --git a/src/dpdk/drivers/net/enic/enic_clsf.c b/src/dpdk/drivers/net/enic/enic_clsf.c
index e6f57bea..8f68faab 100644
--- a/src/dpdk/drivers/net/enic/enic_clsf.c
+++ b/src/dpdk/drivers/net/enic/enic_clsf.c
@@ -38,6 +38,11 @@
#include <rte_malloc.h>
#include <rte_hash.h>
#include <rte_byteorder.h>
+#include <rte_ip.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_sctp.h>
+#include <rte_eth_ctrl.h>
#include "enic_compat.h"
#include "enic.h"
@@ -67,6 +72,296 @@ void enic_fdir_stats_get(struct enic *enic, struct rte_eth_fdir_stats *stats)
*stats = enic->fdir.stats;
}
+void enic_fdir_info_get(struct enic *enic, struct rte_eth_fdir_info *info)
+{
+ info->mode = enic->fdir.modes;
+ info->flow_types_mask[0] = enic->fdir.types_mask;
+}
+
+void enic_fdir_info(struct enic *enic)
+{
+ enic->fdir.modes = (u32)RTE_FDIR_MODE_PERFECT;
+ enic->fdir.types_mask = 1 << RTE_ETH_FLOW_NONFRAG_IPV4_UDP |
+ 1 << RTE_ETH_FLOW_NONFRAG_IPV4_TCP;
+ if (enic->adv_filters) {
+ enic->fdir.types_mask |= 1 << RTE_ETH_FLOW_NONFRAG_IPV4_OTHER |
+ 1 << RTE_ETH_FLOW_NONFRAG_IPV4_SCTP |
+ 1 << RTE_ETH_FLOW_NONFRAG_IPV6_UDP |
+ 1 << RTE_ETH_FLOW_NONFRAG_IPV6_TCP |
+ 1 << RTE_ETH_FLOW_NONFRAG_IPV6_SCTP |
+ 1 << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER;
+ enic->fdir.copy_fltr_fn = copy_fltr_v2;
+ } else {
+ enic->fdir.copy_fltr_fn = copy_fltr_v1;
+ }
+}
+
+static void
+enic_set_layer(struct filter_generic_1 *gp, unsigned int flag,
+ enum filter_generic_1_layer layer, void *mask, void *val,
+ unsigned int len)
+{
+ gp->mask_flags |= flag;
+ gp->val_flags |= gp->mask_flags;
+ memcpy(gp->layer[layer].mask, mask, len);
+ memcpy(gp->layer[layer].val, val, len);
+}
+
+
+/* Copy Flow Director filter to a VIC ipv4 filter (for Cisco VICs
+ * without advanced filter support.
+ */
+void
+copy_fltr_v1(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
+ __rte_unused struct rte_eth_fdir_masks *masks)
+{
+ fltr->type = FILTER_IPV4_5TUPLE;
+ fltr->u.ipv4.src_addr = rte_be_to_cpu_32(
+ input->flow.ip4_flow.src_ip);
+ fltr->u.ipv4.dst_addr = rte_be_to_cpu_32(
+ input->flow.ip4_flow.dst_ip);
+ fltr->u.ipv4.src_port = rte_be_to_cpu_16(
+ input->flow.udp4_flow.src_port);
+ fltr->u.ipv4.dst_port = rte_be_to_cpu_16(
+ input->flow.udp4_flow.dst_port);
+
+ if (input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV4_TCP)
+ fltr->u.ipv4.protocol = PROTO_TCP;
+ else
+ fltr->u.ipv4.protocol = PROTO_UDP;
+
+ fltr->u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+}
+
+#define TREX_PATCH
+#ifdef TREX_PATCH
+void
+copy_fltr_recv_all(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
+ struct rte_eth_fdir_masks *masks) {
+ struct filter_generic_1 *gp = &fltr->u.generic_1;
+ memset(gp, 0, sizeof(*gp));
+
+ struct ether_hdr eth_mask, eth_val;
+ memset(&eth_mask, 0, sizeof(eth_mask));
+ memset(&eth_val, 0, sizeof(eth_val));
+
+ eth_val.ether_type = 0x0806;
+ eth_mask.ether_type = 0;
+
+ gp->position = 0;
+ enic_set_layer(gp, 0, FILTER_GENERIC_1_L2,
+ &eth_mask, &eth_val, sizeof(struct ether_hdr));
+
+}
+#endif
+
+/* Copy Flow Director filter to a VIC generic filter (requires advanced
+ * filter support.
+ */
+void
+copy_fltr_v2(struct filter_v2 *fltr, struct rte_eth_fdir_input *input,
+ struct rte_eth_fdir_masks *masks)
+{
+ struct filter_generic_1 *gp = &fltr->u.generic_1;
+ int i;
+
+ RTE_ASSERT(enic->adv_filters);
+
+ fltr->type = FILTER_DPDK_1;
+ memset(gp, 0, sizeof(*gp));
+#ifdef TREX_PATCH
+ // important for this to be below 2.
+ // If added with position 2, IPv6 UDP and ICMP seems to be caught by some other rule
+ gp->position = 1;
+#endif
+
+ if (input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV4_UDP) {
+ struct udp_hdr udp_mask, udp_val;
+ memset(&udp_mask, 0, sizeof(udp_mask));
+ memset(&udp_val, 0, sizeof(udp_val));
+
+ if (input->flow.udp4_flow.src_port) {
+ udp_mask.src_port = masks->src_port_mask;
+ udp_val.src_port = input->flow.udp4_flow.src_port;
+ }
+ if (input->flow.udp4_flow.dst_port) {
+ udp_mask.src_port = masks->dst_port_mask;
+ udp_val.dst_port = input->flow.udp4_flow.dst_port;
+ }
+
+ enic_set_layer(gp, FILTER_GENERIC_1_UDP, FILTER_GENERIC_1_L4,
+ &udp_mask, &udp_val, sizeof(struct udp_hdr));
+ } else if (input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV4_TCP) {
+ struct tcp_hdr tcp_mask, tcp_val;
+ memset(&tcp_mask, 0, sizeof(tcp_mask));
+ memset(&tcp_val, 0, sizeof(tcp_val));
+
+ if (input->flow.tcp4_flow.src_port) {
+ tcp_mask.src_port = masks->src_port_mask;
+ tcp_val.src_port = input->flow.tcp4_flow.src_port;
+ }
+ if (input->flow.tcp4_flow.dst_port) {
+ tcp_mask.dst_port = masks->dst_port_mask;
+ tcp_val.dst_port = input->flow.tcp4_flow.dst_port;
+ }
+
+ enic_set_layer(gp, FILTER_GENERIC_1_TCP, FILTER_GENERIC_1_L4,
+ &tcp_mask, &tcp_val, sizeof(struct tcp_hdr));
+ } else if (input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV4_SCTP) {
+ struct sctp_hdr sctp_mask, sctp_val;
+ memset(&sctp_mask, 0, sizeof(sctp_mask));
+ memset(&sctp_val, 0, sizeof(sctp_val));
+
+ if (input->flow.sctp4_flow.src_port) {
+ sctp_mask.src_port = masks->src_port_mask;
+ sctp_val.src_port = input->flow.sctp4_flow.src_port;
+ }
+ if (input->flow.sctp4_flow.dst_port) {
+ sctp_mask.dst_port = masks->dst_port_mask;
+ sctp_val.dst_port = input->flow.sctp4_flow.dst_port;
+ }
+ if (input->flow.sctp4_flow.verify_tag) {
+ sctp_mask.tag = 0xffffffff;
+ sctp_val.tag = input->flow.sctp4_flow.verify_tag;
+ }
+
+ /* v4 proto should be 132, override ip4_flow.proto */
+ input->flow.ip4_flow.proto = 132;
+
+ enic_set_layer(gp, 0, FILTER_GENERIC_1_L4, &sctp_mask,
+ &sctp_val, sizeof(struct sctp_hdr));
+ }
+
+ if (input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV4_UDP ||
+ input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV4_TCP ||
+ input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV4_SCTP ||
+ input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV4_OTHER) {
+ struct ipv4_hdr ip4_mask, ip4_val;
+ memset(&ip4_mask, 0, sizeof(struct ipv4_hdr));
+ memset(&ip4_val, 0, sizeof(struct ipv4_hdr));
+
+ if (input->flow.ip4_flow.tos) {
+ ip4_mask.type_of_service = masks->ipv4_mask.tos;
+ ip4_val.type_of_service = input->flow.ip4_flow.tos;
+ }
+ if (input->flow.ip4_flow.ip_id) {
+ ip4_mask.packet_id = 0xffff;
+ ip4_val.packet_id = input->flow.ip4_flow.ip_id;
+ }
+ if (input->flow.ip4_flow.ttl) {
+ ip4_mask.time_to_live = 0xff;
+ ip4_val.time_to_live = input->flow.ip4_flow.ttl;
+ }
+ if (input->flow.ip4_flow.proto) {
+ ip4_mask.next_proto_id = 0xff;
+ ip4_val.next_proto_id = input->flow.ip4_flow.proto;
+ }
+ if (input->flow.ip4_flow.src_ip) {
+ ip4_mask.src_addr = masks->ipv4_mask.src_ip;
+ ip4_val.src_addr = input->flow.ip4_flow.src_ip;
+ }
+ if (input->flow.ip4_flow.dst_ip) {
+ ip4_mask.dst_addr = masks->ipv4_mask.dst_ip;
+ ip4_val.dst_addr = input->flow.ip4_flow.dst_ip;
+ }
+
+ enic_set_layer(gp, FILTER_GENERIC_1_IPV4, FILTER_GENERIC_1_L3,
+ &ip4_mask, &ip4_val, sizeof(struct ipv4_hdr));
+ }
+
+ if (input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV6_UDP) {
+ struct udp_hdr udp_mask, udp_val;
+ memset(&udp_mask, 0, sizeof(udp_mask));
+ memset(&udp_val, 0, sizeof(udp_val));
+
+ if (input->flow.udp6_flow.src_port) {
+ udp_mask.src_port = masks->src_port_mask;
+ udp_val.src_port = input->flow.udp6_flow.src_port;
+ }
+ if (input->flow.udp6_flow.dst_port) {
+ udp_mask.dst_port = masks->dst_port_mask;
+ udp_val.dst_port = input->flow.udp6_flow.dst_port;
+ }
+ enic_set_layer(gp, FILTER_GENERIC_1_UDP, FILTER_GENERIC_1_L4,
+ &udp_mask, &udp_val, sizeof(struct udp_hdr));
+ } else if (input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV6_TCP) {
+ struct tcp_hdr tcp_mask, tcp_val;
+ memset(&tcp_mask, 0, sizeof(tcp_mask));
+ memset(&tcp_val, 0, sizeof(tcp_val));
+
+ if (input->flow.tcp6_flow.src_port) {
+ tcp_mask.src_port = masks->src_port_mask;
+ tcp_val.src_port = input->flow.tcp6_flow.src_port;
+ }
+ if (input->flow.tcp6_flow.dst_port) {
+ tcp_mask.dst_port = masks->dst_port_mask;
+ tcp_val.dst_port = input->flow.tcp6_flow.dst_port;
+ }
+ enic_set_layer(gp, FILTER_GENERIC_1_TCP, FILTER_GENERIC_1_L4,
+ &tcp_mask, &tcp_val, sizeof(struct tcp_hdr));
+ } else if (input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV6_SCTP) {
+ struct sctp_hdr sctp_mask, sctp_val;
+ memset(&sctp_mask, 0, sizeof(sctp_mask));
+ memset(&sctp_val, 0, sizeof(sctp_val));
+
+ if (input->flow.sctp6_flow.src_port) {
+ sctp_mask.src_port = masks->src_port_mask;
+ sctp_val.src_port = input->flow.sctp6_flow.src_port;
+ }
+ if (input->flow.sctp6_flow.dst_port) {
+ sctp_mask.dst_port = masks->dst_port_mask;
+ sctp_val.dst_port = input->flow.sctp6_flow.dst_port;
+ }
+ if (input->flow.sctp6_flow.verify_tag) {
+ sctp_mask.tag = 0xffffffff;
+ sctp_val.tag = input->flow.sctp6_flow.verify_tag;
+ }
+
+ /* v4 proto should be 132, override ipv6_flow.proto */
+ input->flow.ipv6_flow.proto = 132;
+
+ enic_set_layer(gp, 0, FILTER_GENERIC_1_L4, &sctp_mask,
+ &sctp_val, sizeof(struct sctp_hdr));
+ }
+
+ if (input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV6_UDP ||
+ input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV6_TCP ||
+ input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV6_SCTP ||
+ input->flow_type == RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) {
+ struct ipv6_hdr ipv6_mask, ipv6_val;
+ memset(&ipv6_mask, 0, sizeof(struct ipv6_hdr));
+ memset(&ipv6_val, 0, sizeof(struct ipv6_hdr));
+
+ if (input->flow.ipv6_flow.proto) {
+ ipv6_mask.proto = masks->ipv6_mask.proto;
+ ipv6_val.proto = input->flow.ipv6_flow.proto;
+ }
+ for (i = 0; i < 4; i++) {
+ *(uint32_t *)&ipv6_mask.src_addr[i * 4] =
+ masks->ipv6_mask.src_ip[i];
+ *(uint32_t *)&ipv6_val.src_addr[i * 4] =
+ input->flow.ipv6_flow.src_ip[i];
+ }
+ for (i = 0; i < 4; i++) {
+ *(uint32_t *)&ipv6_mask.dst_addr[i * 4] =
+ masks->ipv6_mask.src_ip[i];
+ *(uint32_t *)&ipv6_val.dst_addr[i * 4] =
+ input->flow.ipv6_flow.dst_ip[i];
+ }
+ if (input->flow.ipv6_flow.tc) {
+ ipv6_mask.vtc_flow = ((uint32_t)masks->ipv6_mask.tc<<12);
+ ipv6_val.vtc_flow = input->flow.ipv6_flow.tc << 12;
+ }
+ if (input->flow.ipv6_flow.hop_limits) {
+ ipv6_mask.hop_limits = 0xff;
+ ipv6_val.hop_limits = input->flow.ipv6_flow.hop_limits;
+ }
+
+ enic_set_layer(gp, FILTER_GENERIC_1_IPV6, FILTER_GENERIC_1_L3,
+ &ipv6_mask, &ipv6_val, sizeof(struct ipv6_hdr));
+ }
+}
+
int enic_fdir_del_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
{
int32_t pos;
@@ -77,11 +372,23 @@ int enic_fdir_del_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
case -EINVAL:
case -ENOENT:
enic->fdir.stats.f_remove++;
+#ifdef TREX_PATCH
+ return pos;
+#else
return -EINVAL;
+#endif
default:
/* The entry is present in the table */
key = enic->fdir.nodes[pos];
+#ifdef TREX_PATCH
+ switch (params->soft_id) {
+ case 100:
+ // remove promisc when we delete 'receive all' filter
+ vnic_dev_packet_filter(enic->vdev, 1, 1, 1, 0, 1);
+ break;
+ }
+#endif
/* Delete the filter */
vnic_dev_classifier(enic->vdev, CLSF_DEL,
&key->fltr_id, NULL);
@@ -97,7 +404,7 @@ int enic_fdir_del_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
{
struct enic_fdir_node *key;
- struct filter fltr = {0};
+ struct filter_v2 fltr;
int32_t pos;
u8 do_free = 0;
u16 old_fltr_id = 0;
@@ -105,9 +412,9 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
u16 flex_bytes;
u16 queue;
- flowtype_supported = (
- (RTE_ETH_FLOW_NONFRAG_IPV4_TCP == params->input.flow_type) ||
- (RTE_ETH_FLOW_NONFRAG_IPV4_UDP == params->input.flow_type));
+ memset(&fltr, 0, sizeof(fltr));
+ flowtype_supported = enic->fdir.types_mask
+ & (1 << params->input.flow_type);
flex_bytes = ((params->input.flow_ext.flexbytes[1] << 8 & 0xFF00) |
(params->input.flow_ext.flexbytes[0] & 0xFF));
@@ -120,7 +427,12 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
return -ENOTSUP;
}
- queue = params->action.rx_queue;
+ /* Get the enicpmd RQ from the DPDK Rx queue */
+ queue = enic_sop_rq(params->action.rx_queue);
+
+ if (!enic->rq[queue].in_use)
+ return -EINVAL;
+
/* See if the key is already there in the table */
pos = rte_hash_del_key(enic->fdir.hash, params);
switch (pos) {
@@ -183,22 +495,19 @@ int enic_fdir_add_fltr(struct enic *enic, struct rte_eth_fdir_filter *params)
key->filter = *params;
key->rq_index = queue;
- fltr.type = FILTER_IPV4_5TUPLE;
- fltr.u.ipv4.src_addr = rte_be_to_cpu_32(
- params->input.flow.ip4_flow.src_ip);
- fltr.u.ipv4.dst_addr = rte_be_to_cpu_32(
- params->input.flow.ip4_flow.dst_ip);
- fltr.u.ipv4.src_port = rte_be_to_cpu_16(
- params->input.flow.udp4_flow.src_port);
- fltr.u.ipv4.dst_port = rte_be_to_cpu_16(
- params->input.flow.udp4_flow.dst_port);
-
- if (RTE_ETH_FLOW_NONFRAG_IPV4_TCP == params->input.flow_type)
- fltr.u.ipv4.protocol = PROTO_TCP;
- else
- fltr.u.ipv4.protocol = PROTO_UDP;
-
- fltr.u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE;
+#ifdef TREX_PATCH
+ switch (params->soft_id) {
+ case 100:
+ vnic_dev_packet_filter(enic->vdev, 1, 1, 1, 1, 1);
+ copy_fltr_recv_all(&fltr, &params->input, &enic->rte_dev->data->dev_conf.fdir_conf.mask);
+ break;
+ default:
+#endif
+ enic->fdir.copy_fltr_fn(&fltr, &params->input,
+ &enic->rte_dev->data->dev_conf.fdir_conf.mask);
+#ifdef TREX_PATCH
+ }
+#endif
if (!vnic_dev_classifier(enic->vdev, CLSF_ADD, &queue, &fltr)) {
key->fltr_id = queue;
@@ -238,6 +547,7 @@ void enic_clsf_destroy(struct enic *enic)
vnic_dev_classifier(enic->vdev, CLSF_DEL,
&key->fltr_id, NULL);
rte_free(key);
+ enic->fdir.nodes[index] = NULL;
}
}
diff --git a/src/dpdk/drivers/net/enic/enic_ethdev.c b/src/dpdk/drivers/net/enic/enic_ethdev.c
index 47b07c92..6a86e23f 100644
--- a/src/dpdk/drivers/net/enic/enic_ethdev.c
+++ b/src/dpdk/drivers/net/enic/enic_ethdev.c
@@ -95,10 +95,12 @@ enicpmd_fdir_ctrl_func(struct rte_eth_dev *eth_dev,
break;
case RTE_ETH_FILTER_FLUSH:
- case RTE_ETH_FILTER_INFO:
dev_warning(enic, "unsupported operation %u", filter_op);
ret = -ENOTSUP;
break;
+ case RTE_ETH_FILTER_INFO:
+ enic_fdir_info_get(enic, (struct rte_eth_fdir_info *)arg);
+ break;
default:
dev_err(enic, "unknown operation %u", filter_op);
ret = -EINVAL;
@@ -433,6 +435,9 @@ static void enicpmd_dev_stats_reset(struct rte_eth_dev *eth_dev)
enic_dev_stats_clear(enic);
}
+
+
+
static void enicpmd_dev_info_get(struct rte_eth_dev *eth_dev,
struct rte_eth_dev_info *device_info)
{
@@ -459,6 +464,8 @@ static void enicpmd_dev_info_get(struct rte_eth_dev *eth_dev,
device_info->default_rxconf = (struct rte_eth_rxconf) {
.rx_free_thresh = ENIC_DEFAULT_RX_FREE_THRESH
};
+
+ device_info->speed_capa = ETH_LINK_SPEED_40G;
}
static const uint32_t *enicpmd_dev_supported_ptypes_get(struct rte_eth_dev *dev)
diff --git a/src/dpdk/drivers/net/enic/enic_main.c b/src/dpdk/drivers/net/enic/enic_main.c
index b4ca3710..473bfc3c 100644
--- a/src/dpdk/drivers/net/enic/enic_main.c
+++ b/src/dpdk/drivers/net/enic/enic_main.c
@@ -166,6 +166,7 @@ void enic_dev_stats_get(struct enic *enic, struct rte_eth_stats *r_stats)
return;
}
+
/* The number of truncated packets can only be calculated by
* subtracting a hardware counter from error packets received by
* the driver. Note: this causes transient inaccuracies in the
@@ -180,7 +181,7 @@ void enic_dev_stats_get(struct enic *enic, struct rte_eth_stats *r_stats)
r_stats->ipackets = stats->rx.rx_frames_ok - rx_truncated;
r_stats->opackets = stats->tx.tx_frames_ok;
- r_stats->ibytes = stats->rx.rx_bytes_ok;
+ r_stats->ibytes = stats->rx.rx_unicast_bytes_ok+stats->rx.rx_multicast_bytes_ok+stats->rx.rx_broadcast_bytes_ok;
r_stats->obytes = stats->tx.tx_bytes_ok;
r_stats->ierrors = stats->rx.rx_errors + stats->rx.rx_drop;
@@ -1122,6 +1123,9 @@ static int enic_dev_init(struct enic *enic)
return err;
}
+ /* Get the supported filters */
+ enic_fdir_info(enic);
+
eth_dev->data->mac_addrs = rte_zmalloc("enic_mac_addr", ETH_ALEN, 0);
if (!eth_dev->data->mac_addrs) {
dev_err(enic, "mac addr storage alloc failed, aborting.\n");
diff --git a/src/dpdk/drivers/net/enic/enic_res.c b/src/dpdk/drivers/net/enic/enic_res.c
index 84c5d336..8a230a16 100644
--- a/src/dpdk/drivers/net/enic/enic_res.c
+++ b/src/dpdk/drivers/net/enic/enic_res.c
@@ -62,6 +62,7 @@ int enic_get_vnic_config(struct enic *enic)
return err;
}
+
#define GET_CONFIG(m) \
do { \
err = vnic_dev_spec(enic->vdev, \
@@ -98,6 +99,10 @@ int enic_get_vnic_config(struct enic *enic)
enic->rte_dev->data->mtu = min_t(u16, enic->max_mtu,
max_t(u16, ENIC_MIN_MTU, c->mtu));
+ enic->adv_filters = vnic_dev_capable_adv_filters(enic->vdev);
+ dev_info(enic, "Advanced Filters %savailable\n", ((enic->adv_filters)
+ ? "" : "not "));
+
c->wq_desc_count =
min_t(u32, ENIC_MAX_WQ_DESCS,
max_t(u32, ENIC_MIN_WQ_DESCS,
diff --git a/src/dpdk/drivers/net/ixgbe/ixgbe_ethdev.c b/src/dpdk/drivers/net/ixgbe/ixgbe_ethdev.c
index d478a159..72963a89 100644
--- a/src/dpdk/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/src/dpdk/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -5784,13 +5784,17 @@ ixgbe_add_del_ethertype_filter(struct rte_eth_dev *dev,
if (filter->queue >= IXGBE_MAX_RX_QUEUE_NUM)
return -EINVAL;
-
+#define TREX_PATCH
+#ifndef TREX_PATCH
+ // no real reason to block this.
+ // We configure rules using FDIR and ethertype that point to same queue, so there are no race condition issues.
if (filter->ether_type == ETHER_TYPE_IPv4 ||
filter->ether_type == ETHER_TYPE_IPv6) {
PMD_DRV_LOG(ERR, "unsupported ether_type(0x%04x) in"
" ethertype filter.", filter->ether_type);
return -EINVAL;
}
+#endif
if (filter->flags & RTE_ETHTYPE_FLAGS_MAC) {
PMD_DRV_LOG(ERR, "mac compare is unsupported.");
diff --git a/src/dpdk/drivers/net/mlx5/mlx5.c b/src/dpdk/drivers/net/mlx5/mlx5.c
index d96a9aff..303b917b 100644
--- a/src/dpdk/drivers/net/mlx5/mlx5.c
+++ b/src/dpdk/drivers/net/mlx5/mlx5.c
@@ -181,6 +181,9 @@ mlx5_dev_close(struct rte_eth_dev *dev)
}
if (priv->reta_idx != NULL)
rte_free(priv->reta_idx);
+
+ mlx5_stats_free(dev);
+
priv_unlock(priv);
memset(priv, 0, sizeof(*priv));
}
@@ -366,6 +369,13 @@ mlx5_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
unsigned int mps;
int idx;
int i;
+ static int ibv_was_init=0;
+
+ if (ibv_was_init==0) {
+ ibv_fork_init();
+ ibv_was_init=1;
+ }
+
(void)pci_drv;
assert(pci_drv == &mlx5_driver.pci_drv);
@@ -511,7 +521,16 @@ mlx5_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
priv->mtu = ETHER_MTU;
priv->mps = mps; /* Enable MPW by default if supported. */
priv->cqe_comp = 1; /* Enable compression by default. */
+
+
err = mlx5_args(priv, pci_dev->devargs);
+
+ /* TREX PATCH */
+ /* set for maximum performance default */
+ priv->txq_inline =128;
+ priv->txqs_inline =4;
+
+
if (err) {
ERROR("failed to process device arguments: %s",
strerror(err));
@@ -751,7 +770,6 @@ rte_mlx5_pmd_init(const char *name, const char *args)
* using this PMD, which is not supported in forked processes.
*/
setenv("RDMAV_HUGEPAGES_SAFE", "1", 1);
- ibv_fork_init();
rte_eal_pci_register(&mlx5_driver.pci_drv);
return 0;
}
diff --git a/src/dpdk/drivers/net/mlx5/mlx5.h b/src/dpdk/drivers/net/mlx5/mlx5.h
index 3a866098..68bad904 100644
--- a/src/dpdk/drivers/net/mlx5/mlx5.h
+++ b/src/dpdk/drivers/net/mlx5/mlx5.h
@@ -84,6 +84,34 @@ enum {
PCI_DEVICE_ID_MELLANOX_CONNECTX4LXVF = 0x1016,
};
+struct mlx5_stats_priv {
+
+ struct rte_eth_stats m_shadow;
+ uint32_t n_stats; /* number of counters */
+
+ void * et_stats ;/* point to ethtool counter struct ethtool_stats*/
+
+ /* index into ethtool */
+ uint16_t inx_rx_vport_unicast_bytes;
+ uint16_t inx_rx_vport_multicast_bytes;
+ uint16_t inx_rx_vport_broadcast_bytes;
+ uint16_t inx_rx_vport_unicast_packets;
+ uint16_t inx_rx_vport_multicast_packets;
+ uint16_t inx_rx_vport_broadcast_packets;
+ uint16_t inx_tx_vport_unicast_bytes;
+ uint16_t inx_tx_vport_multicast_bytes;
+ uint16_t inx_tx_vport_broadcast_bytes;
+ uint16_t inx_tx_vport_unicast_packets;
+ uint16_t inx_tx_vport_multicast_packets;
+ uint16_t inx_tx_vport_broadcast_packets;
+ uint16_t inx_rx_wqe_err;
+ uint16_t inx_rx_crc_errors_phy;
+ uint16_t inx_rx_in_range_len_errors_phy;
+ uint16_t inx_rx_symbol_err_phy;
+ uint16_t inx_tx_errors_phy;
+};
+
+
struct priv {
struct rte_eth_dev *dev; /* Ethernet device. */
struct ibv_context *ctx; /* Verbs context. */
@@ -135,6 +163,7 @@ struct priv {
unsigned int reta_idx_n; /* RETA index size. */
struct fdir_filter_list *fdir_filter_list; /* Flow director rules. */
rte_spinlock_t lock; /* Lock for control functions. */
+ struct mlx5_stats_priv m_stats;
};
/* Local storage for secondary process data. */
@@ -243,6 +272,8 @@ void mlx5_allmulticast_disable(struct rte_eth_dev *);
void mlx5_stats_get(struct rte_eth_dev *, struct rte_eth_stats *);
void mlx5_stats_reset(struct rte_eth_dev *);
+void mlx5_stats_free(struct rte_eth_dev *dev);
+
/* mlx5_vlan.c */
diff --git a/src/dpdk/drivers/net/mlx5/mlx5_autoconf.h b/src/dpdk/drivers/net/mlx5/mlx5_autoconf.h
new file mode 100644
index 00000000..9fdfff84
--- /dev/null
+++ b/src/dpdk/drivers/net/mlx5/mlx5_autoconf.h
@@ -0,0 +1,8 @@
+#ifndef HAVE_VERBS_IBV_EXP_CQ_COMPRESSED_CQE
+#define HAVE_VERBS_IBV_EXP_CQ_COMPRESSED_CQE 1
+#endif /* HAVE_VERBS_IBV_EXP_CQ_COMPRESSED_CQE */
+
+#ifndef HAVE_VERBS_MLX5_ETH_VLAN_INLINE_HEADER_SIZE
+#define HAVE_VERBS_MLX5_ETH_VLAN_INLINE_HEADER_SIZE 1
+#endif /* HAVE_VERBS_MLX5_ETH_VLAN_INLINE_HEADER_SIZE */
+
diff --git a/src/dpdk/drivers/net/mlx5/mlx5_fdir.c b/src/dpdk/drivers/net/mlx5/mlx5_fdir.c
index 73eb00ec..4ba3bb9f 100644
--- a/src/dpdk/drivers/net/mlx5/mlx5_fdir.c
+++ b/src/dpdk/drivers/net/mlx5/mlx5_fdir.c
@@ -37,12 +37,14 @@
#include <string.h>
#include <errno.h>
+#define TREX_PATCH
+
/* Verbs header. */
/* ISO C doesn't support unnamed structs/unions, disabling -pedantic. */
#ifdef PEDANTIC
#pragma GCC diagnostic ignored "-pedantic"
#endif
-#include <infiniband/verbs.h>
+#include <infiniband/verbs_exp.h>
#ifdef PEDANTIC
#pragma GCC diagnostic error "-pedantic"
#endif
@@ -67,6 +69,10 @@ struct fdir_flow_desc {
uint16_t src_port;
uint32_t src_ip[4];
uint32_t dst_ip[4];
+ uint8_t tos;
+ uint8_t ip_id;
+ uint8_t proto;
+
uint8_t mac[6];
uint16_t vlan_tag;
enum hash_rxq_type type;
@@ -102,6 +108,7 @@ fdir_filter_to_flow_desc(const struct rte_eth_fdir_filter *fdir_filter,
desc->vlan_tag = fdir_filter->input.flow_ext.vlan_tci;
/* Set MAC address. */
+#ifndef TREX_PATCH
if (mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) {
rte_memcpy(desc->mac,
fdir_filter->input.flow.mac_vlan_flow.mac_addr.
@@ -110,6 +117,13 @@ fdir_filter_to_flow_desc(const struct rte_eth_fdir_filter *fdir_filter,
desc->type = HASH_RXQ_ETH;
return;
}
+#else
+ if (fdir_filter->input.flow.ip4_flow.ip_id == 2) {
+ desc->type = HASH_RXQ_ETH;
+ desc->ip_id = fdir_filter->input.flow.ip4_flow.ip_id;
+ return;
+ }
+#endif
/* Set mode */
switch (fdir_filter->input.flow_type) {
@@ -141,9 +155,13 @@ fdir_filter_to_flow_desc(const struct rte_eth_fdir_filter *fdir_filter,
case RTE_ETH_FLOW_NONFRAG_IPV4_TCP:
desc->src_port = fdir_filter->input.flow.udp4_flow.src_port;
desc->dst_port = fdir_filter->input.flow.udp4_flow.dst_port;
+
case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER:
desc->src_ip[0] = fdir_filter->input.flow.ip4_flow.src_ip;
desc->dst_ip[0] = fdir_filter->input.flow.ip4_flow.dst_ip;
+ desc->tos = fdir_filter->input.flow.ip4_flow.ttl; /* TTL is mapped to TOS TREX_PATCH */
+ desc->ip_id = fdir_filter->input.flow.ip4_flow.ip_id;
+ desc->proto = fdir_filter->input.flow.ip4_flow.proto;
break;
case RTE_ETH_FLOW_NONFRAG_IPV6_UDP:
case RTE_ETH_FLOW_NONFRAG_IPV6_TCP:
@@ -157,12 +175,17 @@ fdir_filter_to_flow_desc(const struct rte_eth_fdir_filter *fdir_filter,
rte_memcpy(desc->dst_ip,
fdir_filter->input.flow.ipv6_flow.dst_ip,
sizeof(desc->dst_ip));
+ desc->tos = (uint8_t)fdir_filter->input.flow.ipv6_flow.hop_limits; /* TTL is mapped to TOS - TREX_PATCH */
+ desc->ip_id = (uint8_t)fdir_filter->input.flow.ipv6_flow.flow_label;
+ desc->proto = fdir_filter->input.flow.ipv6_flow.proto;
+
break;
default:
break;
}
}
+
/**
* Check if two flow descriptors overlap according to configured mask.
*
@@ -197,6 +220,12 @@ priv_fdir_overlap(const struct priv *priv,
((desc1->dst_port & mask->dst_port_mask) !=
(desc2->dst_port & mask->dst_port_mask)))
return 0;
+
+ if ( (desc1->tos != desc2->tos) ||
+ (desc1->ip_id != desc2->ip_id) ||
+ (desc1->proto != desc2->proto) )
+ return 0;
+
switch (desc1->type) {
case HASH_RXQ_IPV4:
case HASH_RXQ_UDPV4:
@@ -204,8 +233,9 @@ priv_fdir_overlap(const struct priv *priv,
if (((desc1->src_ip[0] & mask->ipv4_mask.src_ip) !=
(desc2->src_ip[0] & mask->ipv4_mask.src_ip)) ||
((desc1->dst_ip[0] & mask->ipv4_mask.dst_ip) !=
- (desc2->dst_ip[0] & mask->ipv4_mask.dst_ip)))
+ (desc2->dst_ip[0] & mask->ipv4_mask.dst_ip)))
return 0;
+
break;
case HASH_RXQ_IPV6:
case HASH_RXQ_UDPV6:
@@ -251,8 +281,8 @@ priv_fdir_flow_add(struct priv *priv,
struct ibv_exp_flow_attr *attr = &data->attr;
uintptr_t spec_offset = (uintptr_t)&data->spec;
struct ibv_exp_flow_spec_eth *spec_eth;
- struct ibv_exp_flow_spec_ipv4 *spec_ipv4;
- struct ibv_exp_flow_spec_ipv6 *spec_ipv6;
+ struct ibv_exp_flow_spec_ipv4_ext *spec_ipv4;
+ struct ibv_exp_flow_spec_ipv6_ext *spec_ipv6;
struct ibv_exp_flow_spec_tcp_udp *spec_tcp_udp;
struct mlx5_fdir_filter *iter_fdir_filter;
unsigned int i;
@@ -264,8 +294,10 @@ priv_fdir_flow_add(struct priv *priv,
(iter_fdir_filter->flow != NULL) &&
(priv_fdir_overlap(priv,
&mlx5_fdir_filter->desc,
- &iter_fdir_filter->desc)))
- return EEXIST;
+ &iter_fdir_filter->desc))){
+ ERROR("overlap rules, please check your rules");
+ return EEXIST;
+ }
/*
* No padding must be inserted by the compiler between attr and spec.
@@ -288,6 +320,7 @@ priv_fdir_flow_add(struct priv *priv,
/* Update priority */
attr->priority = 2;
+#ifndef TREX_PATCH
if (fdir_mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) {
/* MAC Address */
for (i = 0; i != RTE_DIM(spec_eth->mask.dst_mac); ++i) {
@@ -297,6 +330,14 @@ priv_fdir_flow_add(struct priv *priv,
}
goto create_flow;
}
+#else
+ // empty mask means "match everything". This rule will match all packets, no matter what is the ether type
+ if (desc->ip_id == 2) {
+ spec_eth->val.ether_type = 0x0806;
+ spec_eth->mask.ether_type = 0x0000;
+ goto create_flow;
+ }
+#endif
switch (desc->type) {
case HASH_RXQ_IPV4:
@@ -305,10 +346,10 @@ priv_fdir_flow_add(struct priv *priv,
spec_offset += spec_eth->size;
/* Set IP spec */
- spec_ipv4 = (struct ibv_exp_flow_spec_ipv4 *)spec_offset;
+ spec_ipv4 = (struct ibv_exp_flow_spec_ipv4_ext *)spec_offset;
/* The second specification must be IP. */
- assert(spec_ipv4->type == IBV_EXP_FLOW_SPEC_IPV4);
+ assert(spec_ipv4->type == IBV_EXP_FLOW_SPEC_IPV4_EXT);
assert(spec_ipv4->size == sizeof(*spec_ipv4));
spec_ipv4->val.src_ip =
@@ -318,6 +359,21 @@ priv_fdir_flow_add(struct priv *priv,
spec_ipv4->mask.src_ip = mask->ipv4_mask.src_ip;
spec_ipv4->mask.dst_ip = mask->ipv4_mask.dst_ip;
+ /* PROTO */
+ spec_ipv4->val.proto = desc->proto & mask->ipv4_mask.proto;
+ spec_ipv4->mask.proto = mask->ipv4_mask.proto;
+
+#ifdef TREX_PATCH
+ /* TOS */
+ if (desc->ip_id == 1) {
+ spec_ipv4->mask.tos = 0x1;
+ spec_ipv4->val.tos = 0x1;
+ } else {
+ spec_ipv4->mask.tos = 0x0;
+ spec_ipv4->val.tos = 0x0;
+ }
+ // spec_ipv4->val.tos = desc->tos & spec_ipv4->mask.tos;// & mask->ipv4_mask.tos;
+#endif
/* Update priority */
attr->priority = 1;
@@ -332,10 +388,10 @@ priv_fdir_flow_add(struct priv *priv,
spec_offset += spec_eth->size;
/* Set IP spec */
- spec_ipv6 = (struct ibv_exp_flow_spec_ipv6 *)spec_offset;
+ spec_ipv6 = (struct ibv_exp_flow_spec_ipv6_ext *)spec_offset;
/* The second specification must be IP. */
- assert(spec_ipv6->type == IBV_EXP_FLOW_SPEC_IPV6);
+ assert(spec_ipv6->type == IBV_EXP_FLOW_SPEC_IPV6_EXT);
assert(spec_ipv6->size == sizeof(*spec_ipv6));
for (i = 0; i != RTE_DIM(desc->src_ip); ++i) {
@@ -351,6 +407,20 @@ priv_fdir_flow_add(struct priv *priv,
mask->ipv6_mask.dst_ip,
sizeof(spec_ipv6->mask.dst_ip));
+ spec_ipv6->val.next_hdr = desc->proto & mask->ipv6_mask.proto;
+ spec_ipv6->mask.next_hdr = mask->ipv6_mask.proto;
+
+#ifdef TREX_PATCH
+ /* TOS */
+ if (desc->ip_id == 1) {
+ spec_ipv6->mask.traffic_class = 0x1;
+ spec_ipv6->val.traffic_class = 0x1;
+ } else {
+ spec_ipv6->mask.traffic_class = 0;
+ spec_ipv6->val.traffic_class = 0;
+ }
+#endif
+
/* Update priority */
attr->priority = 1;
@@ -722,8 +792,10 @@ priv_fdir_filter_add(struct priv *priv,
/* Duplicate filters are currently unsupported. */
mlx5_fdir_filter = priv_find_filter_in_list(priv, fdir_filter);
if (mlx5_fdir_filter != NULL) {
+#ifndef TREX_PATCH
ERROR("filter already exists");
- return EINVAL;
+#endif
+ return EEXIST;
}
/* Create new flow director filter. */
@@ -847,9 +919,11 @@ priv_fdir_filter_delete(struct priv *priv,
return 0;
}
+#ifndef TREX_PATCH
ERROR("%p: flow director delete failed, cannot find filter",
(void *)priv);
- return EINVAL;
+#endif
+ return ENOENT;
}
/**
diff --git a/src/dpdk/drivers/net/mlx5/mlx5_rxq.c b/src/dpdk/drivers/net/mlx5/mlx5_rxq.c
index 29c137cd..6be01d39 100644
--- a/src/dpdk/drivers/net/mlx5/mlx5_rxq.c
+++ b/src/dpdk/drivers/net/mlx5/mlx5_rxq.c
@@ -102,7 +102,7 @@ const struct hash_rxq_init hash_rxq_init[] = {
ETH_RSS_FRAG_IPV4),
.flow_priority = 1,
.flow_spec.ipv4 = {
- .type = IBV_EXP_FLOW_SPEC_IPV4,
+ .type = IBV_EXP_FLOW_SPEC_IPV4_EXT,
.size = sizeof(hash_rxq_init[0].flow_spec.ipv4),
},
.underlayer = &hash_rxq_init[HASH_RXQ_ETH],
@@ -140,7 +140,7 @@ const struct hash_rxq_init hash_rxq_init[] = {
ETH_RSS_FRAG_IPV6),
.flow_priority = 1,
.flow_spec.ipv6 = {
- .type = IBV_EXP_FLOW_SPEC_IPV6,
+ .type = IBV_EXP_FLOW_SPEC_IPV6_EXT,
.size = sizeof(hash_rxq_init[0].flow_spec.ipv6),
},
.underlayer = &hash_rxq_init[HASH_RXQ_ETH],
diff --git a/src/dpdk/drivers/net/mlx5/mlx5_rxtx.c b/src/dpdk/drivers/net/mlx5/mlx5_rxtx.c
index fce3381a..c0bcfd03 100644
--- a/src/dpdk/drivers/net/mlx5/mlx5_rxtx.c
+++ b/src/dpdk/drivers/net/mlx5/mlx5_rxtx.c
@@ -908,7 +908,7 @@ mlx5_mpw_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
mpw->wqe->mpw.eseg.rsvd2 = 0;
mpw->wqe->mpw.ctrl.data[0] = htonl((MLX5_OPC_MOD_MPW << 24) |
(txq->wqe_ci << 8) |
- MLX5_OPCODE_LSO_MPW);
+ MLX5_OPCODE_TSO);
mpw->wqe->mpw.ctrl.data[2] = 0;
mpw->wqe->mpw.ctrl.data[3] = 0;
mpw->data.dseg[0] = &mpw->wqe->mpw.dseg[0];
@@ -1107,7 +1107,7 @@ mlx5_mpw_inline_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
mpw->wqe = &(*txq->wqes)[idx];
mpw->wqe->mpw_inl.ctrl.data[0] = htonl((MLX5_OPC_MOD_MPW << 24) |
(txq->wqe_ci << 8) |
- MLX5_OPCODE_LSO_MPW);
+ MLX5_OPCODE_TSO);
mpw->wqe->mpw_inl.ctrl.data[2] = 0;
mpw->wqe->mpw_inl.ctrl.data[3] = 0;
mpw->wqe->mpw_inl.eseg.mss = htons(length);
diff --git a/src/dpdk/drivers/net/mlx5/mlx5_rxtx.h b/src/dpdk/drivers/net/mlx5/mlx5_rxtx.h
index f6e2cbac..d87dd19b 100644
--- a/src/dpdk/drivers/net/mlx5/mlx5_rxtx.h
+++ b/src/dpdk/drivers/net/mlx5/mlx5_rxtx.h
@@ -173,8 +173,8 @@ struct hash_rxq_init {
uint16_t size;
} hdr;
struct ibv_exp_flow_spec_tcp_udp tcp_udp;
- struct ibv_exp_flow_spec_ipv4 ipv4;
- struct ibv_exp_flow_spec_ipv6 ipv6;
+ struct ibv_exp_flow_spec_ipv4_ext ipv4;
+ struct ibv_exp_flow_spec_ipv6_ext ipv6;
struct ibv_exp_flow_spec_eth eth;
} flow_spec; /* Flow specification template. */
const struct hash_rxq_init *underlayer; /* Pointer to underlayer. */
diff --git a/src/dpdk/drivers/net/mlx5/mlx5_stats.c b/src/dpdk/drivers/net/mlx5/mlx5_stats.c
index 2d3cb519..788ef939 100644
--- a/src/dpdk/drivers/net/mlx5/mlx5_stats.c
+++ b/src/dpdk/drivers/net/mlx5/mlx5_stats.c
@@ -44,6 +44,10 @@
#include "mlx5_rxtx.h"
#include "mlx5_defs.h"
+
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+
/**
* DPDK callback to get device statistics.
*
@@ -52,60 +56,241 @@
* @param[out] stats
* Stats structure output buffer.
*/
+
+
+static void
+mlx5_stats_read_hw(struct rte_eth_dev *dev,
+ struct rte_eth_stats *stats){
+ struct priv *priv = mlx5_get_priv(dev);
+ struct mlx5_stats_priv * lps = &priv->m_stats;
+ unsigned int i;
+
+ struct rte_eth_stats tmp = {0};
+ struct ethtool_stats *et_stats = (struct ethtool_stats *)lps->et_stats;
+ struct ifreq ifr;
+
+ et_stats->cmd = ETHTOOL_GSTATS;
+ et_stats->n_stats = lps->n_stats;
+
+ ifr.ifr_data = (caddr_t) et_stats;
+
+ if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
+ WARN("unable to get statistic values for mlnx5 ");
+ }
+
+ tmp.ibytes += et_stats->data[lps->inx_rx_vport_unicast_bytes] +
+ et_stats->data[lps->inx_rx_vport_multicast_bytes] +
+ et_stats->data[lps->inx_rx_vport_broadcast_bytes];
+
+ tmp.ipackets += et_stats->data[lps->inx_rx_vport_unicast_packets] +
+ et_stats->data[lps->inx_rx_vport_multicast_packets] +
+ et_stats->data[lps->inx_rx_vport_broadcast_packets];
+
+ tmp.ierrors += (et_stats->data[lps->inx_rx_wqe_err] +
+ et_stats->data[lps->inx_rx_crc_errors_phy] +
+ et_stats->data[lps->inx_rx_in_range_len_errors_phy] +
+ et_stats->data[lps->inx_rx_symbol_err_phy]);
+
+ tmp.obytes += et_stats->data[lps->inx_tx_vport_unicast_bytes] +
+ et_stats->data[lps->inx_tx_vport_multicast_bytes] +
+ et_stats->data[lps->inx_tx_vport_broadcast_bytes];
+
+ tmp.opackets += (et_stats->data[lps->inx_tx_vport_unicast_packets] +
+ et_stats->data[lps->inx_tx_vport_multicast_packets] +
+ et_stats->data[lps->inx_tx_vport_broadcast_packets]);
+
+ tmp.oerrors += et_stats->data[lps->inx_tx_errors_phy];
+
+ /* SW Rx */
+ for (i = 0; (i != priv->rxqs_n); ++i) {
+ struct rxq *rxq = (*priv->rxqs)[i];
+ if (rxq) {
+ tmp.imissed += rxq->stats.idropped;
+ tmp.rx_nombuf += rxq->stats.rx_nombuf;
+ }
+ }
+
+ /*SW Tx */
+ for (i = 0; (i != priv->txqs_n); ++i) {
+ struct txq *txq = (*priv->txqs)[i];
+ if (txq) {
+ tmp.oerrors += txq->stats.odropped;
+ }
+ }
+
+ *stats =tmp;
+}
+
+void
+mlx5_stats_free(struct rte_eth_dev *dev)
+{
+ struct priv *priv = mlx5_get_priv(dev);
+ struct mlx5_stats_priv * lps = &priv->m_stats;
+
+ if ( lps->et_stats ){
+ free(lps->et_stats);
+ lps->et_stats=0;
+ }
+}
+
+
+static void
+mlx5_stats_init(struct rte_eth_dev *dev)
+{
+ struct priv *priv = mlx5_get_priv(dev);
+ struct mlx5_stats_priv * lps = &priv->m_stats;
+ struct rte_eth_stats tmp = {0};
+
+ unsigned int i;
+ unsigned int idx;
+ char ifname[IF_NAMESIZE];
+ struct ifreq ifr;
+
+ struct ethtool_stats *et_stats = NULL;
+ struct ethtool_drvinfo drvinfo;
+ struct ethtool_gstrings *strings = NULL;
+ unsigned int n_stats, sz_str, sz_stats;
+
+ if (priv_get_ifname(priv, &ifname)) {
+ WARN("unable to get interface name");
+ return;
+ }
+ /* How many statistics are available ? */
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ ifr.ifr_data = (caddr_t) &drvinfo;
+ if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
+ WARN("unable to get driver info for %s", ifname);
+ return;
+ }
+
+ n_stats = drvinfo.n_stats;
+ if (n_stats < 1) {
+ WARN("no statistics available for %s", ifname);
+ return;
+ }
+ lps->n_stats = n_stats;
+
+ /* Allocate memory to grab stat names and values */
+ sz_str = n_stats * ETH_GSTRING_LEN;
+ sz_stats = n_stats * sizeof(uint64_t);
+ strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings));
+ if (!strings) {
+ WARN("unable to allocate memory for strings");
+ return;
+ }
+
+ et_stats = calloc(1, sz_stats + sizeof(struct ethtool_stats));
+ if (!et_stats) {
+ free(strings);
+ WARN("unable to allocate memory for stats");
+ }
+
+ strings->cmd = ETHTOOL_GSTRINGS;
+ strings->string_set = ETH_SS_STATS;
+ strings->len = n_stats;
+ ifr.ifr_data = (caddr_t) strings;
+ if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
+ WARN("unable to get statistic names for %s", ifname);
+ free(strings);
+ free(et_stats);
+ return;
+ }
+
+ for (i = 0; (i != n_stats); ++i) {
+
+ const char * curr_string = (const char*) &(strings->data[i * ETH_GSTRING_LEN]);
+
+ if (!strcmp("rx_vport_unicast_bytes", curr_string)) lps->inx_rx_vport_unicast_bytes = i;
+ if (!strcmp("rx_vport_multicast_bytes", curr_string)) lps->inx_rx_vport_multicast_bytes = i;
+ if (!strcmp("rx_vport_broadcast_bytes", curr_string)) lps->inx_rx_vport_broadcast_bytes = i;
+
+ if (!strcmp("rx_vport_unicast_packets", curr_string)) lps->inx_rx_vport_unicast_packets = i;
+ if (!strcmp("rx_vport_multicast_packets", curr_string)) lps->inx_rx_vport_multicast_packets = i;
+ if (!strcmp("rx_vport_broadcast_packets", curr_string)) lps->inx_rx_vport_broadcast_packets = i;
+
+ if (!strcmp("tx_vport_unicast_bytes", curr_string)) lps->inx_tx_vport_unicast_bytes = i;
+ if (!strcmp("tx_vport_multicast_bytes", curr_string)) lps->inx_tx_vport_multicast_bytes = i;
+ if (!strcmp("tx_vport_broadcast_bytes", curr_string)) lps->inx_tx_vport_broadcast_bytes = i;
+
+ if (!strcmp("tx_vport_unicast_packets", curr_string)) lps->inx_tx_vport_unicast_packets = i;
+ if (!strcmp("tx_vport_multicast_packets", curr_string)) lps->inx_tx_vport_multicast_packets = i;
+ if (!strcmp("tx_vport_broadcast_packets", curr_string)) lps->inx_tx_vport_broadcast_packets = i;
+
+ if (!strcmp("rx_wqe_err", curr_string)) lps->inx_rx_wqe_err = i;
+ if (!strcmp("rx_crc_errors_phy", curr_string)) lps->inx_rx_crc_errors_phy = i;
+ if (!strcmp("rx_in_range_len_errors_phy", curr_string)) lps->inx_rx_in_range_len_errors_phy = i;
+ if (!strcmp("rx_symbol_err_phy", curr_string)) lps->inx_rx_symbol_err_phy = i;
+
+ if (!strcmp("tx_errors_phy", curr_string)) lps->inx_tx_errors_phy = i;
+ }
+
+ lps->et_stats =(void *)et_stats;
+
+ if (!lps->inx_rx_vport_unicast_bytes ||
+ !lps->inx_rx_vport_multicast_bytes ||
+ !lps->inx_rx_vport_broadcast_bytes ||
+ !lps->inx_rx_vport_unicast_packets ||
+ !lps->inx_rx_vport_multicast_packets ||
+ !lps->inx_rx_vport_broadcast_packets ||
+ !lps->inx_tx_vport_unicast_bytes ||
+ !lps->inx_tx_vport_multicast_bytes ||
+ !lps->inx_tx_vport_broadcast_bytes ||
+ !lps->inx_tx_vport_unicast_packets ||
+ !lps->inx_tx_vport_multicast_packets ||
+ !lps->inx_tx_vport_broadcast_packets ||
+ !lps->inx_rx_wqe_err ||
+ !lps->inx_rx_crc_errors_phy ||
+ !lps->inx_rx_in_range_len_errors_phy) {
+ WARN("Counters are not recognized %s", ifname);
+ return;
+ }
+
+ mlx5_stats_read_hw(dev,&tmp);
+
+ /* copy yo shadow at first time */
+ lps->m_shadow = tmp;
+
+ free(strings);
+}
+
+
+static void
+mlx5_stats_diff(struct rte_eth_stats *a,
+ struct rte_eth_stats *b,
+ struct rte_eth_stats *c){
+ #define MLX5_DIFF(cnt) { a->cnt = (b->cnt - c->cnt); }
+
+ MLX5_DIFF(ipackets);
+ MLX5_DIFF(opackets);
+ MLX5_DIFF(ibytes);
+ MLX5_DIFF(obytes);
+ MLX5_DIFF(imissed);
+
+ MLX5_DIFF(ierrors);
+ MLX5_DIFF(oerrors);
+ MLX5_DIFF(rx_nombuf);
+}
+
+
void
mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
{
struct priv *priv = mlx5_get_priv(dev);
- struct rte_eth_stats tmp = {0};
- unsigned int i;
- unsigned int idx;
-
- priv_lock(priv);
- /* Add software counters. */
- for (i = 0; (i != priv->rxqs_n); ++i) {
- struct rxq *rxq = (*priv->rxqs)[i];
-
- if (rxq == NULL)
- continue;
- idx = rxq->stats.idx;
- if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
-#ifdef MLX5_PMD_SOFT_COUNTERS
- tmp.q_ipackets[idx] += rxq->stats.ipackets;
- tmp.q_ibytes[idx] += rxq->stats.ibytes;
-#endif
- tmp.q_errors[idx] += (rxq->stats.idropped +
- rxq->stats.rx_nombuf);
- }
-#ifdef MLX5_PMD_SOFT_COUNTERS
- tmp.ipackets += rxq->stats.ipackets;
- tmp.ibytes += rxq->stats.ibytes;
-#endif
- tmp.ierrors += rxq->stats.idropped;
- tmp.rx_nombuf += rxq->stats.rx_nombuf;
- }
- for (i = 0; (i != priv->txqs_n); ++i) {
- struct txq *txq = (*priv->txqs)[i];
-
- if (txq == NULL)
- continue;
- idx = txq->stats.idx;
- if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
-#ifdef MLX5_PMD_SOFT_COUNTERS
- tmp.q_opackets[idx] += txq->stats.opackets;
- tmp.q_obytes[idx] += txq->stats.obytes;
-#endif
- tmp.q_errors[idx] += txq->stats.odropped;
- }
-#ifdef MLX5_PMD_SOFT_COUNTERS
- tmp.opackets += txq->stats.opackets;
- tmp.obytes += txq->stats.obytes;
-#endif
- tmp.oerrors += txq->stats.odropped;
- }
-#ifndef MLX5_PMD_SOFT_COUNTERS
- /* FIXME: retrieve and add hardware counters. */
-#endif
- *stats = tmp;
+
+ struct mlx5_stats_priv * lps = &priv->m_stats;
+ priv_lock(priv);
+
+ if (lps->et_stats == NULL) {
+ mlx5_stats_init(dev);
+ }
+ struct rte_eth_stats tmp = {0};
+
+ mlx5_stats_read_hw(dev,&tmp);
+
+ mlx5_stats_diff(stats,
+ &tmp,
+ &lps->m_shadow);
+
priv_unlock(priv);
}
@@ -119,26 +304,20 @@ void
mlx5_stats_reset(struct rte_eth_dev *dev)
{
struct priv *priv = dev->data->dev_private;
- unsigned int i;
- unsigned int idx;
-
- priv_lock(priv);
- for (i = 0; (i != priv->rxqs_n); ++i) {
- if ((*priv->rxqs)[i] == NULL)
- continue;
- idx = (*priv->rxqs)[i]->stats.idx;
- (*priv->rxqs)[i]->stats =
- (struct mlx5_rxq_stats){ .idx = idx };
- }
- for (i = 0; (i != priv->txqs_n); ++i) {
- if ((*priv->txqs)[i] == NULL)
- continue;
- idx = (*priv->txqs)[i]->stats.idx;
- (*priv->txqs)[i]->stats =
- (struct mlx5_txq_stats){ .idx = idx };
- }
-#ifndef MLX5_PMD_SOFT_COUNTERS
- /* FIXME: reset hardware counters. */
-#endif
+ struct mlx5_stats_priv * lps = &priv->m_stats;
+
+ priv_lock(priv);
+
+ if (lps->et_stats == NULL) {
+ mlx5_stats_init(dev);
+ }
+ struct rte_eth_stats tmp = {0};
+
+
+ mlx5_stats_read_hw(dev,&tmp);
+
+ /* copy to shadow */
+ lps->m_shadow = tmp;
+
priv_unlock(priv);
}
diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp
index 84be590f..92cbca6e 100644
--- a/src/flow_stat.cpp
+++ b/src/flow_stat.cpp
@@ -461,6 +461,7 @@ CFlowStatRuleMgr::CFlowStatRuleMgr() {
memset(m_rx_cant_count_err, 0, sizeof(m_rx_cant_count_err));
memset(m_tx_cant_count_err, 0, sizeof(m_tx_cant_count_err));
m_num_ports = 0; // need to call create to init
+ m_mode = FLOW_STAT_MODE_NORMAL;
}
CFlowStatRuleMgr::~CFlowStatRuleMgr() {
@@ -802,11 +803,14 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) {
#endif
if (m_num_started_streams == 0) {
+
send_start_stop_msg_to_rx(true); // First transmitting stream. Rx core should start reading packets;
+
//also good time to zero global counters
memset(m_rx_cant_count_err, 0, sizeof(m_rx_cant_count_err));
memset(m_tx_cant_count_err, 0, sizeof(m_tx_cant_count_err));
+ #if 0
// wait to make sure that message is acknowledged. RX core might be in deep sleep mode, and we want to
// start transmitting packets only after it is working, otherwise, packets will get lost.
if (m_rx_core) { // in simulation, m_rx_core will be NULL
@@ -819,6 +823,8 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) {
}
}
}
+ #endif
+
} else {
// make sure rx core is working. If not, we got really confused somehow.
if (m_rx_core)
@@ -966,13 +972,24 @@ int CFlowStatRuleMgr::set_mode(enum flow_stat_mode_e mode) {
extern bool rx_should_stop;
void CFlowStatRuleMgr::send_start_stop_msg_to_rx(bool is_start) {
TrexStatelessCpToRxMsgBase *msg;
-
+
if (is_start) {
- msg = new TrexStatelessRxStartMsg();
+ static MsgReply<bool> reply;
+ reply.reset();
+
+ msg = new TrexStatelessRxEnableLatency(reply);
+ m_ring_to_rx->Enqueue((CGenNode *)msg);
+
+ /* hold until message was ack'ed - otherwise we might lose packets */
+ if (m_rx_core) {
+ reply.wait_for_reply();
+ assert(m_rx_core->is_working());
+ }
+
} else {
- msg = new TrexStatelessRxStopMsg();
+ msg = new TrexStatelessRxDisableLatency();
+ m_ring_to_rx->Enqueue((CGenNode *)msg);
}
- m_ring_to_rx->Enqueue((CGenNode *)msg);
}
// return false if no counters changed since last run. true otherwise
diff --git a/src/flow_stat_parser.cpp b/src/flow_stat_parser.cpp
index 7335a6a2..4a6722e6 100644
--- a/src/flow_stat_parser.cpp
+++ b/src/flow_stat_parser.cpp
@@ -27,6 +27,7 @@
#include "common/Network/Packet/TcpHeader.h"
#include "pkt_gen.h"
#include "flow_stat_parser.h"
+#include "bp_sim.h"
void CFlowStatParser::reset() {
m_start = 0;
@@ -120,12 +121,15 @@ int CFlowStatParser::get_ip_id(uint32_t &ip_id) {
int CFlowStatParser::set_ip_id(uint32_t new_id) {
if (m_ipv4) {
// Updating checksum, not recalculating, so if someone put bad checksum on purpose, it will stay bad
+ m_ipv4->updateCheckSum(PKT_NTOHS(m_ipv4->getFirstWord()), PKT_NTOHS(m_ipv4->getFirstWord() |TOS_TTL_RESERVE_DUPLICATE));
m_ipv4->updateCheckSum(PKT_NTOHS(m_ipv4->getId()), PKT_NTOHS(new_id));
m_ipv4->setId(new_id);
+ m_ipv4->setTOS(m_ipv4->getTOS()|TOS_TTL_RESERVE_DUPLICATE);
return 0;
}
if (m_ipv6) {
+ m_ipv6->setTrafficClass(m_ipv6->getTrafficClass()|TOS_TTL_RESERVE_DUPLICATE);
m_ipv6->setFlowLabel(new_id);
return 0;
}
diff --git a/src/gtest/client_cfg_test.cpp b/src/gtest/client_cfg_test.cpp
new file mode 100644
index 00000000..4e93f3c5
--- /dev/null
+++ b/src/gtest/client_cfg_test.cpp
@@ -0,0 +1,186 @@
+/*
+ Ido Barnea
+
+ Cisco Systems, Inc.
+*/
+
+/*
+Copyright (c) 2016-2016 Cisco Systems, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+#include <stdio.h>
+#include "../bp_sim.h"
+#include <common/gtest.h>
+#include <common/basic_utils.h>
+
+class basic_client_cfg : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ }
+ virtual void TearDown() {
+ }
+ public:
+};
+
+TEST_F(basic_client_cfg, test1) {
+ uint32_t ip_start = 0x10010101;
+ uint32_t ip_end = 0x100101ff;
+ uint32_t next_hop_init = 0x01010101;
+ uint32_t next_hop_resp = 0x02020202;
+ uint16_t vlan_init = 5;
+ uint16_t vlan_resp = 7;
+ uint32_t dual_if_mask = 0x01000000;
+ uint32_t test_count = 2;
+ ClientCfgDB cfg_db;
+ ClientCfgDB test_db;
+ ClientCfgEntry cfg_ent;
+ ClientCfgExt cfg_ext;
+ std::vector<ClientCfgCompactEntry *> ent_list;
+ struct CTupleGenPoolYaml c_pool;
+
+ // Create tuple gen, so we can have ip->port translation
+ CTupleGenYamlInfo tg_yam_info;
+ struct CTupleGenPoolYaml s_pool;
+ s_pool.m_ip_start = ip_start;
+ s_pool.m_ip_end = ip_end;
+ s_pool.m_dual_interface_mask = dual_if_mask;
+ tg_yam_info.m_client_pool.push_back(s_pool);
+
+ CGlobalInfo::m_options.m_expected_portd = 4;
+ printf("Expected ports %d\n", CGlobalInfo::m_options.m_expected_portd);
+
+ std::string tmp_file_name = "client_cfg_gtest_GENERATED.yaml";
+ FILE *fd = fopen(tmp_file_name.c_str(), "w");
+
+ if (fd == NULL) {
+ fprintf(stderr, "Failed opening %s file for write\n", tmp_file_name.c_str());
+ }
+
+ // We create config file with 3 groups (Should match 6 ports).
+ cfg_ext.m_initiator.set_next_hop(next_hop_init);
+ cfg_ext.m_responder.set_next_hop(next_hop_resp);
+ cfg_ext.m_initiator.set_vlan(vlan_init);
+ cfg_ext.m_responder.set_vlan(vlan_resp);
+
+ cfg_ent.set_params(ip_start, ip_end, test_count);
+ cfg_ent.set_cfg(cfg_ext);
+
+ // first group
+ cfg_db.set_vlan(true);
+ cfg_db.add_group(ip_start, cfg_ent);
+
+ //second group
+ cfg_ent.set_params(ip_start + dual_if_mask, ip_end + dual_if_mask
+ , test_count);
+ cfg_db.add_group(ip_start + dual_if_mask, cfg_ent);
+
+ // third group
+ cfg_ent.set_params(ip_start + 2 * dual_if_mask, ip_end + 2 * dual_if_mask
+ , test_count);
+ cfg_db.add_group(ip_start + dual_if_mask * 2, cfg_ent);
+
+ cfg_db.dump(fd);
+ fclose(fd);
+ test_db.load_yaml_file(tmp_file_name);
+ test_db.set_tuple_gen_info(&tg_yam_info);
+ test_db.get_entry_list(ent_list);
+
+
+ // We expect ports for first two groups to be found.
+ // This group addresses should not appear in the list, since
+ // we simulate system with only 4 ports
+ int i = 0;
+ for (std::vector<ClientCfgCompactEntry *>::iterator
+ it = ent_list.begin(); it != ent_list.end(); it++) {
+ uint8_t port = (*it)->get_port();
+ uint16_t vlan = (*it)->get_vlan();
+ uint32_t count = (*it)->get_count();
+ uint32_t dst_ip = (*it)->get_dst_ip();
+
+ assert(count == test_count);
+ switch(i) {
+ case 0:
+ case 2:
+ assert(port == i);
+ assert(vlan == vlan_init);
+ assert(dst_ip == next_hop_init);
+ break;
+ case 1:
+ case 3:
+ assert(port == i);
+ assert(vlan == vlan_resp);
+ assert(dst_ip == next_hop_resp);
+ break;
+ default:
+ fprintf(stderr, "Test failed. Too many entries returned\n");
+ exit(1);
+ }
+ i++;
+ delete *it;
+ }
+
+ // Simulate the pre test phase, and hand results to client config
+ CManyIPInfo many_ip;
+ MacAddress mac0, mac1, mac2, mac3;
+ mac0.set(0x0, 0x1, 0x2, 0x3, 0x4, 0);
+ mac1.set(0x0, 0x1, 0x2, 0x3, 0x4, 0x1);
+ mac2.set(0x0, 0x1, 0x2, 0x3, 0x4, 0x2);
+ mac3.set(0x0, 0x1, 0x2, 0x3, 0x4, 0x3);
+ COneIPv4Info ip0_1(next_hop_init, vlan_init, mac0, 0);
+ COneIPv4Info ip0_2(next_hop_init + 1, vlan_init, mac1, 0);
+ COneIPv4Info ip1_1(next_hop_resp, vlan_resp, mac2, 1);
+ COneIPv4Info ip1_2(next_hop_resp + 1, vlan_resp, mac3, 1);
+
+ many_ip.insert(ip0_1);
+ many_ip.insert(ip0_2);
+ many_ip.insert(ip1_1);
+ many_ip.insert(ip1_2);
+
+ test_db.set_resolved_macs(many_ip);
+
+ ClientCfgBase cfg0;
+
+ ClientCfgEntry *ent0 = test_db.lookup(ip_start);
+ ClientCfgEntry *ent1 = test_db.lookup(ip_start + dual_if_mask);
+
+ assert (ent0 != NULL);
+ ent0->assign(cfg0);
+ assert (!memcmp(cfg0.m_initiator.get_dst_mac_addr()
+ , mac0.GetConstBuffer(), ETHER_ADDR_LEN));
+ ent0->assign(cfg0);
+ assert (!memcmp(cfg0.m_initiator.get_dst_mac_addr()
+ , mac1.GetConstBuffer(), ETHER_ADDR_LEN));
+ ent0->assign(cfg0);
+ assert (!memcmp(cfg0.m_responder.get_dst_mac_addr()
+ , mac2.GetConstBuffer(), ETHER_ADDR_LEN));
+ ent0->assign(cfg0);
+ assert (!memcmp(cfg0.m_responder.get_dst_mac_addr()
+ , mac3.GetConstBuffer(), ETHER_ADDR_LEN));
+
+ assert(ent1 != NULL);
+ ent1->assign(cfg0);
+ assert (!memcmp(cfg0.m_initiator.get_dst_mac_addr()
+ , mac0.GetConstBuffer(), ETHER_ADDR_LEN));
+ ent1->assign(cfg0);
+ assert (!memcmp(cfg0.m_initiator.get_dst_mac_addr()
+ , mac1.GetConstBuffer(), ETHER_ADDR_LEN));
+ ent1->assign(cfg0);
+ assert (!memcmp(cfg0.m_responder.get_dst_mac_addr()
+ , mac2.GetConstBuffer(), ETHER_ADDR_LEN));
+ ent1->assign(cfg0);
+ assert (!memcmp(cfg0.m_responder.get_dst_mac_addr()
+ , mac3.GetConstBuffer(), ETHER_ADDR_LEN));
+
+}
+
diff --git a/src/gtest/trex_stateless_gtest.cpp b/src/gtest/trex_stateless_gtest.cpp
index 09942c57..2593261f 100644
--- a/src/gtest/trex_stateless_gtest.cpp
+++ b/src/gtest/trex_stateless_gtest.cpp
@@ -4340,6 +4340,7 @@ TEST_F(basic_stl, pcap_remote_basic) {
0,
"exp/remote_test.cap",
10,
+ 0,
1,
1,
-1,
@@ -4363,6 +4364,7 @@ TEST_F(basic_stl, pcap_remote_loop) {
0,
"exp/remote_test.cap",
1,
+ 0,
1,
3,
-1,
@@ -4385,6 +4387,7 @@ TEST_F(basic_stl, pcap_remote_duration) {
0,
"exp/remote_test.cap",
100000,
+ 0,
1,
0,
0.5,
@@ -4407,6 +4410,7 @@ TEST_F(basic_stl, pcap_remote_dual) {
0,
"exp/remote_test_dual.erf",
10000,
+ 0,
1,
0,
0.5,
diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h
index 631f9a3e..5723503c 100644
--- a/src/internal_api/trex_platform_api.h
+++ b/src/internal_api/trex_platform_api.h
@@ -28,6 +28,7 @@ limitations under the License.
#include <string.h>
#include "flow_stat_parser.h"
#include "trex_defs.h"
+#include "trex_stateless_rx_defs.h"
#include "trex_port_attr.h"
#include <json/json.h>
@@ -112,19 +113,13 @@ public:
IF_STAT_RX_BYTES_COUNT = 8, // Card support counting rx bytes
};
- struct mac_cfg_st {
- uint8_t hw_macaddr[6];
- uint8_t src_macaddr[6];
- uint8_t dst_macaddr[6];
- };
-
/**
* interface static info
*
*/
struct intf_info_st {
std::string driver_name;
- mac_cfg_st mac_info;
+ uint8_t hw_macaddr[6];
std::string pci_addr;
int numa_node;
bool has_crc;
@@ -234,7 +229,7 @@ public:
info.has_crc = true;
info.numa_node = 0;
- memset(&info.mac_info, 0, sizeof(info.mac_info));
+ memset(&info.hw_macaddr, 0, sizeof(info.hw_macaddr));
}
virtual void get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const {
diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp
index 16fc2203..f8f365c8 100644
--- a/src/main_dpdk.cpp
+++ b/src/main_dpdk.cpp
@@ -96,6 +96,7 @@ extern "C" {
#define RTE_TEST_RX_DESC_DEFAULT 64
#define RTE_TEST_RX_LATENCY_DESC_DEFAULT (1*1024)
+#define RTE_TEST_RX_DESC_DEFAULT_MLX 8
#define RTE_TEST_RX_DESC_VM_DEFAULT 512
#define RTE_TEST_TX_DESC_VM_DEFAULT 512
@@ -147,7 +148,7 @@ public:
}
virtual int configure_rx_filter_rules(CPhyEthIF * _if)=0;
virtual int add_del_rx_flow_stat_rule(uint8_t port_id, enum rte_filter_op op, uint16_t l3, uint8_t l4
- , uint8_t ipv6_next_h, uint16_t id) {return -1;};
+ , uint8_t ipv6_next_h, uint16_t id) {return 0;}
virtual bool is_hardware_support_drop_queue(){
return(false);
}
@@ -168,14 +169,24 @@ public:
virtual CFlowStatParser *get_flow_stat_parser();
virtual int set_rcv_all(CPhyEthIF * _if, bool set_on)=0;
virtual TRexPortAttr * create_port_attr(uint8_t port_id) = 0;
+ virtual uint8_t get_num_crc_fix_bytes() {return 0;}
- /* Does this NIC type support automatic packet dropping in case of a link down?
+ /* Does this NIC type support automatic packet dropping in case of a link down?
in case it is supported the packets will be dropped, else there would be a back pressure to tx queues
- this interface is used as a workaround to let TRex work without link in stateless mode, driver that
+ this interface is used as a workaround to let TRex work without link in stateless mode, driver that
does not support that will be failed at init time because it will cause watchdog due to watchdog hang */
virtual bool drop_packets_incase_of_linkdown() {
return (false);
}
+
+ /* Mellanox ConnectX-4 can drop only 35MPPS per Rx queue. to workaround this issue we will create multi rx queue and enable RSS. for Queue1 we will disable RSS
+ return zero for disable patch and rx queues number for enable
+ */
+
+ virtual uint16_t enable_rss_drop_workaround(void) {
+ return (0);
+ }
+
};
@@ -277,6 +288,18 @@ public:
virtual int set_rcv_all(CPhyEthIF * _if, bool set_on) {return 0;}
};
+class CTRexExtendedDriverBaseE1000 : public CTRexExtendedDriverBase1GVm {
+ CTRexExtendedDriverBaseE1000() {
+ // E1000 driver is only relevant in VM in our case
+ CGlobalInfo::m_options.preview.set_vm_one_queue_enable(true);
+ }
+public:
+ static CTRexExtendedDriverBase * create() {
+ return ( new CTRexExtendedDriverBaseE1000() );
+ }
+ // e1000 driver handing us packets with ethernet CRC, so we need to chop them
+ virtual uint8_t get_num_crc_fix_bytes() {return 4;}
+};
class CTRexExtendedDriverBase10G : public CTRexExtendedDriverBase {
public:
@@ -316,7 +339,8 @@ public:
| TrexPlatformApi::IF_STAT_PAYLOAD;
}
virtual CFlowStatParser *get_flow_stat_parser();
- virtual int set_rcv_all(CPhyEthIF * _if, bool set_on) {return 0;}
+ int add_del_eth_filter(CPhyEthIF * _if, bool is_add, uint16_t ethertype);
+ virtual int set_rcv_all(CPhyEthIF * _if, bool set_on);
};
class CTRexExtendedDriverBase40G : public CTRexExtendedDriverBase10G {
@@ -367,7 +391,7 @@ public:
private:
virtual void add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, uint8_t ttl
- , uint16_t ip_id, uint16_t l4_proto, int queue, uint16_t stat_idx);
+ , uint16_t ip_id, uint8_t l4_proto, int queue, uint16_t stat_idx);
virtual int add_del_eth_type_rule(uint8_t port_id, enum rte_filter_op op, uint16_t eth_type);
virtual int configure_rx_filter_rules_statefull(CPhyEthIF * _if);
@@ -379,7 +403,7 @@ private:
uint8_t m_if_per_card;
};
-class CTRexExtendedDriverBaseVIC : public CTRexExtendedDriverBase40G {
+class CTRexExtendedDriverBaseVIC : public CTRexExtendedDriverBase {
public:
CTRexExtendedDriverBaseVIC(){
}
@@ -393,15 +417,101 @@ public:
}
virtual bool is_hardware_filter_is_supported(){
- return (false);
+ return (true);
+ }
+ virtual void update_global_config_fdir(port_cfg_t * cfg){
}
- virtual int verify_fw_ver(int i) {return 0;}
+
+ virtual bool is_hardware_support_drop_queue(){
+ return(true);
+ }
+
+ void clear_extended_stats(CPhyEthIF * _if);
+
+ void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats);
+
+
+ virtual int get_min_sample_rate(void){
+ return (RX_CHECK_MIX_SAMPLE_RATE);
+ }
+
+ virtual int verify_fw_ver(int i);
virtual void update_configuration(port_cfg_t * cfg);
+
+ virtual int configure_rx_filter_rules(CPhyEthIF * _if);
+ virtual void reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len);
+ virtual int get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts, uint32_t *bytes, uint32_t *prev_bytes, int min, int max);
+ virtual int get_stat_counters_num() {return MAX_FLOW_STATS;}
+ virtual int get_rx_stat_capabilities() {
+ return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_PAYLOAD;
+ }
+ virtual CFlowStatParser *get_flow_stat_parser();
+ virtual int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd);
+ virtual int set_rcv_all(CPhyEthIF * _if, bool set_on);
+
+private:
+
+ virtual void add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, uint16_t id
+ , uint8_t l4_proto, uint8_t tos, int queue);
+ virtual int add_del_eth_type_rule(uint8_t port_id, enum rte_filter_op op, uint16_t eth_type);
+ virtual int configure_rx_filter_rules_statefull(CPhyEthIF * _if);
+
};
+class CTRexExtendedDriverBaseMlnx5G : public CTRexExtendedDriverBase10G {
+public:
+ CTRexExtendedDriverBaseMlnx5G(){
+ }
+
+ TRexPortAttr * create_port_attr(uint8_t port_id) {
+ // disabling flow control on 40G using DPDK API causes the interface to malfunction
+ return new DpdkTRexPortAttr(port_id, false, false);
+ }
+
+ static CTRexExtendedDriverBase * create(){
+ return ( new CTRexExtendedDriverBaseMlnx5G() );
+ }
+
+ virtual void update_global_config_fdir(port_cfg_t * cfg){
+ }
+
+ virtual void update_configuration(port_cfg_t * cfg);
+
+ virtual int configure_rx_filter_rules(CPhyEthIF * _if);
+ virtual bool is_hardware_filter_is_supported(){
+ return (true);
+ }
+
+ virtual bool is_hardware_support_drop_queue(){
+ return(true);
+ }
+ virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats);
+ virtual void clear_extended_stats(CPhyEthIF * _if);
+ virtual void reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len);
+ virtual int get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts, uint32_t *bytes, uint32_t *prev_bytes, int min, int max);
+ virtual int dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd);
+ virtual int get_stat_counters_num() {return MAX_FLOW_STATS;}
+ virtual int get_rx_stat_capabilities() {
+ return TrexPlatformApi::IF_STAT_IPV4_ID | TrexPlatformApi::IF_STAT_PAYLOAD;
+ }
+ virtual int wait_for_stable_link();
+ // disabling flow control on 40G using DPDK API causes the interface to malfunction
+ virtual bool flow_control_disable_supported(){return false;}
+ virtual CFlowStatParser *get_flow_stat_parser();
+ virtual int set_rcv_all(CPhyEthIF * _if, bool set_on);
+
+ virtual uint16_t enable_rss_drop_workaround(void) {
+ return (5);
+ }
+
+private:
+ virtual void add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, uint16_t ip_id, uint8_t l4_proto
+ , int queue);
+ virtual int add_del_rx_filter_rules(CPhyEthIF * _if, bool set_on);
+};
typedef CTRexExtendedDriverBase * (*create_object_t) (void);
@@ -453,9 +563,11 @@ private:
register_driver(std::string("rte_igb_pmd"),CTRexExtendedDriverBase1G::create);
register_driver(std::string("rte_i40e_pmd"),CTRexExtendedDriverBase40G::create);
register_driver(std::string("rte_enic_pmd"),CTRexExtendedDriverBaseVIC::create);
+ register_driver(std::string("librte_pmd_mlx5"),CTRexExtendedDriverBaseMlnx5G::create);
+
/* virtual devices */
- register_driver(std::string("rte_em_pmd"),CTRexExtendedDriverBase1GVm::create);
+ register_driver(std::string("rte_em_pmd"),CTRexExtendedDriverBaseE1000::create);
register_driver(std::string("rte_vmxnet3_pmd"),CTRexExtendedDriverBase1GVm::create);
register_driver(std::string("rte_virtio_pmd"),CTRexExtendedDriverBase1GVm::create);
@@ -746,6 +858,7 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
bool latency_was_set=false;
(void)latency_was_set;
char ** rgpszArg = NULL;
+ bool opt_vlan_was_set = false;
int a=0;
int node_dump=0;
@@ -836,9 +949,7 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
po->preview.set_disable_flow_control_setting(true);
break;
case OPT_VLAN:
- if ( get_is_stateless() ) {
- po->preview.set_vlan_mode_enable(true);
- }
+ opt_vlan_was_set = true;
break;
case OPT_LIMT_NUM_OF_PORTS :
po->m_expected_portd =atoi(args.OptionArg());
@@ -872,7 +983,7 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
}
}
if (po->m_run_mode != CParserOption::RUN_MODE_INVALID) {
- parse_err("Please specify single run mode");
+ parse_err("Please specify single run mode (-i for stateless, or -f <file> for stateful");
}
po->m_run_mode = CParserOption::RUN_MODE_DUMP_INFO;
break;
@@ -969,16 +1080,16 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
if ((po->m_run_mode == CParserOption::RUN_MODE_INVALID) ) {
- parse_err("Please provide single run mode (e.g. batch or interactive)");
+ parse_err("Please provide single run mode. -f <file> for stateful or -i for stateless (interactive)");
}
if (CGlobalInfo::is_learn_mode() && po->preview.get_ipv6_mode_enable()) {
- parse_err("--learn mode is not supported with --ipv6, beacuse there is not such thing NAT66 ( ipv6-ipv6) \n" \
- "if you think it is important,open a defect \n");
+ parse_err("--learn mode is not supported with --ipv6, beacuse there is no such thing as NAT66 (ipv6 to ipv6 translation) \n" \
+ "If you think it is important, please open a defect or write to TRex mailing list\n");
}
if (po->preview.get_is_rx_check_enable() || po->is_latency_enabled() || CGlobalInfo::is_learn_mode()
- || (CGlobalInfo::m_options.m_arp_ref_per != 0)) {
+ || (CGlobalInfo::m_options.m_arp_ref_per != 0) || get_vm_one_queue_enable()) {
po->set_rx_enabled();
}
@@ -991,7 +1102,7 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
uint32_t cores=po->preview.getCores();
if ( cores > ((BP_MAX_CORES)/2-1) ) {
- printf(" ERROR maximum supported cores are : %d \n",((BP_MAX_CORES)/2-1));
+ fprintf(stderr, " Error: maximum supported core number is: %d \n",((BP_MAX_CORES)/2-1));
return -1;
}
@@ -1000,7 +1111,7 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
/* only first time read the configuration file */
if ( po->platform_cfg_file.length() >0 ) {
if ( node_dump ){
- printf("Loading platform configuration file from %s \n",po->platform_cfg_file.c_str());
+ printf("Using configuration file %s \n",po->platform_cfg_file.c_str());
}
global_platform_cfg_info.load_from_yaml_file(po->platform_cfg_file);
if ( node_dump ){
@@ -1008,7 +1119,9 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
}
}else{
if ( utl_is_file_exists("/etc/trex_cfg.yaml") ){
- printf("found configuration file at /etc/trex_cfg.yaml \n");
+ if ( node_dump ){
+ printf("Using configuration file /etc/trex_cfg.yaml \n");
+ }
global_platform_cfg_info.load_from_yaml_file("/etc/trex_cfg.yaml");
if ( node_dump ){
global_platform_cfg_info.Dump(stdout);
@@ -1018,20 +1131,26 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
}
if ( get_is_stateless() ) {
+ if ( opt_vlan_was_set ) {
+ po->preview.set_vlan_mode_enable(true);
+ }
+ if (CGlobalInfo::m_options.client_cfg_file != "") {
+ parse_err("Client config file is not supported with interactive (stateless) mode ");
+ }
if ( po->m_duration ) {
- parse_err("Duration is not supported with interactive mode ");
+ parse_err("Duration is not supported with interactive (stateless) mode ");
}
if ( po->preview.get_is_rx_check_enable() ) {
- parse_err("Rx check is not supported with interactive mode ");
+ parse_err("Rx check is not supported with interactive (stateless) mode ");
}
if ( (po->is_latency_enabled()) || (po->preview.getOnlyLatency()) ){
- parse_err("Latency check is not supported with interactive mode ");
+ parse_err("Latency check is not supported with interactive (stateless) mode ");
}
if ( po->preview.getSingleCore() ){
- parse_err("Single core is not supported with interactive mode ");
+ parse_err("Single core is not supported with interactive (stateless) mode ");
}
}
@@ -1519,6 +1638,7 @@ int DpdkTRexPortAttr::set_led(bool on){
int DpdkTRexPortAttr::get_flow_ctrl(int &mode) {
int ret = rte_eth_dev_flow_ctrl_get(m_port_id, &fc_conf_tmp);
if (ret) {
+ mode = -1;
return ret;
}
mode = (int) fc_conf_tmp.mode;
@@ -1615,12 +1735,19 @@ bool DpdkTRexPortAttr::update_link_status_nowait(){
rte_eth_link new_link;
bool changed = false;
rte_eth_link_get_nowait(m_port_id, &new_link);
+
if (new_link.link_speed != m_link.link_speed ||
new_link.link_duplex != m_link.link_duplex ||
new_link.link_autoneg != m_link.link_autoneg ||
new_link.link_status != m_link.link_status) {
changed = true;
+
+ /* in case of link status change - notify the dest object */
+ if (new_link.link_status != m_link.link_status) {
+ get_dest().on_link_down();
+ }
}
+
m_link = new_link;
return changed;
}
@@ -1662,7 +1789,7 @@ bool DpdkTRexPortAttr::get_promiscuous(){
}
-void DpdkTRexPortAttr::macaddr_get(struct ether_addr *mac_addr){
+void DpdkTRexPortAttr::get_hw_src_mac(struct ether_addr *mac_addr){
rte_eth_macaddr_get(m_port_id , mac_addr);
}
@@ -1853,10 +1980,9 @@ public:
virtual int send_node(CGenNode * node);
virtual void send_one_pkt(pkt_dir_t dir, rte_mbuf_t *m);
virtual int flush_tx_queue(void);
- __attribute__ ((noinline)) void handle_rx_queue();
__attribute__ ((noinline)) void handle_slowpath_features(CGenNode *node, rte_mbuf_t *m, uint8_t *p, pkt_dir_t dir);
- void apply_client_cfg(const ClientCfg *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p);
+ void apply_client_cfg(const ClientCfgBase *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p);
bool process_rx_pkt(pkt_dir_t dir,rte_mbuf_t * m);
@@ -1929,46 +2055,6 @@ bool CCoreEthIF::Create(uint8_t core_id,
return (true);
}
-// This function is only relevant if we are in VM. In this case, we only have one rx queue. Can't have
-// rules to drop queue 0 packets, and pass queue 1 packets to RX core, like in other cases.
-// We receive all packets in the same core that transmitted, and handle them to RX core.
-void CCoreEthIF::handle_rx_queue(void) {
- if ( likely( ! get_vm_one_queue_enable() ) ) {
- return;
- }
-
- pkt_dir_t dir;
- bool is_rx = get_is_rx_thread_enabled();
- for (dir=CLIENT_SIDE; dir<CS_NUM; dir++) {
- CCorePerPort * lp_port=&m_ports[dir];
- CPhyEthIF * lp=lp_port->m_port;
-
- rte_mbuf_t * rx_pkts[32];
- int j=0;
-
- while (true) {
- j++;
- uint16_t cnt =lp->rx_burst(0,rx_pkts,32);
- if ( cnt ) {
- int i;
- for (i=0; i<(int)cnt;i++) {
- rte_mbuf_t * m=rx_pkts[i];
- if ( is_rx ){
- if (!process_rx_pkt(dir,m)){
- rte_pktmbuf_free(m);
- }
- }else{
- rte_pktmbuf_free(m);
- }
- }
- }
- if ((cnt<5) || j>10 ) {
- break;
- }
- }
- }
-}
-
int CCoreEthIF::flush_tx_queue(void){
/* flush both sides */
pkt_dir_t dir;
@@ -1981,8 +2067,6 @@ int CCoreEthIF::flush_tx_queue(void){
}
}
- handle_rx_queue();
-
return 0;
}
@@ -2247,12 +2331,12 @@ int CCoreEthIFStateless::handle_slow_path_node(CGenNode * no) {
return (-1);
}
-void CCoreEthIF::apply_client_cfg(const ClientCfg *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p) {
+void CCoreEthIF::apply_client_cfg(const ClientCfgBase *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p) {
assert(cfg);
/* take the right direction config */
- const ClientCfgDir &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder);
+ const ClientCfgDirBase &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder);
/* dst mac */
if (cfg_dir.has_dst_mac_addr()) {
@@ -2472,10 +2556,11 @@ private:
class CLatencyVmPort : public CPortLatencyHWBase {
public:
void Create(uint8_t port_index,CNodeRing * ring,
- CLatencyManager * mgr){
+ CLatencyManager * mgr, CPhyEthIF * p) {
m_dir = (port_index%2);
m_ring_to_dp = ring;
m_mgr = mgr;
+ m_port = p;
}
virtual int tx(rte_mbuf_t * m){
@@ -2502,17 +2587,23 @@ public:
return (-1);
}
- virtual rte_mbuf_t * rx(){
- return (0);
+ virtual rte_mbuf_t * rx() {
+ rte_mbuf_t * rx_pkts[1];
+ uint16_t cnt = m_port->rx_burst(0, rx_pkts, 1);
+ if (cnt) {
+ return (rx_pkts[0]);
+ } else {
+ return (0);
+ }
}
- virtual uint16_t rx_burst(struct rte_mbuf **rx_pkts,
- uint16_t nb_pkts){
- return (0);
+ virtual uint16_t rx_burst(struct rte_mbuf **rx_pkts, uint16_t nb_pkts) {
+ uint16_t cnt = m_port->rx_burst(0, rx_pkts, nb_pkts);
+ return (cnt);
}
-
private:
+ CPhyEthIF * m_port;
uint8_t m_dir;
CNodeRing * m_ring_to_dp; /* ring dp -> latency thread */
CLatencyManager * m_mgr;
@@ -2538,6 +2629,8 @@ public:
float m_total_rx_pps;
float m_cpu_util;
+ bool m_link_up = true;
+ bool m_link_was_down = false;
};
class CGlobalStats {
@@ -2808,8 +2901,11 @@ void CGlobalStats::Dump(FILE *fd,DumpFormat mode){
fprintf (fd," --------------- \n");
for (i=0; i<(int)port_to_show; i++) {
CPerPortStats * lp=&m_port[i];
- fprintf(fd,"port : %d \n",(int)i);
- fprintf(fd,"------------\n");
+ fprintf(fd,"port : %d ",(int)i);
+ if ( ! lp->m_link_up ) {
+ fprintf(fd," (link DOWN)");
+ }
+ fprintf(fd,"\n------------\n");
#define GS_DP_A4(f) fprintf(fd," %-40s : %llu \n",#f, (unsigned long long)lp->f)
#define GS_DP_A(f) if (lp->f) fprintf(fd," %-40s : %llu \n",#f, (unsigned long long)lp->f)
GS_DP_A4(opackets);
@@ -2823,7 +2919,13 @@ void CGlobalStats::Dump(FILE *fd,DumpFormat mode){
}else{
fprintf(fd," %10s ","ports");
for (i=0; i<(int)port_to_show; i++) {
- fprintf(fd,"| %15d ",i);
+ CPerPortStats * lp=&m_port[i];
+ if ( lp->m_link_up ) {
+ fprintf(fd,"| %15d ",i);
+ } else {
+ std::string port_with_state = "(link DOWN) " + std::to_string(i);
+ fprintf(fd,"| %15s ",port_with_state.c_str());
+ }
}
fprintf(fd,"\n");
fprintf(fd," -----------------------------------------------------------------------------------------\n");
@@ -3015,14 +3117,16 @@ public:
void dump_config(FILE *fd);
void dump_links_status(FILE *fd);
+ bool lookup_port_by_mac(const uint8_t *mac, uint8_t &port_id);
+
public:
port_cfg_t m_port_cfg;
uint32_t m_max_ports; /* active number of ports supported options are 2,4,8,10,12 */
uint32_t m_max_cores; /* current number of cores , include master and latency ==> ( master)1+c*(m_max_ports>>1)+1( latency ) */
uint32_t m_cores_mul; /* how cores multipler given c=4 ==> m_cores_mul */
- uint32_t m_max_queues_per_port;
- uint32_t m_cores_to_dual_ports; /* number of ports that will handle dual ports */
- uint16_t m_latency_tx_queue_id;
+ uint32_t m_max_queues_per_port; // Number of TX queues per port
+ uint32_t m_cores_to_dual_ports; /* number of TX cores allocated for each port pair */
+ uint16_t m_rx_core_tx_q_id; /* TX q used by rx core */
// statistic
CPPSMeasure m_cps;
float m_expected_pps;
@@ -3067,64 +3171,195 @@ void CGlobalTRex::pre_test() {
CPretest pretest(m_max_ports);
bool resolve_needed = false;
uint8_t empty_mac[ETHER_ADDR_LEN] = {0,0,0,0,0,0};
+ bool need_grat_arp[TREX_MAX_PORTS];
+
+ if (CGlobalInfo::m_options.preview.get_is_client_cfg_enable()) {
+ std::vector<ClientCfgCompactEntry *> conf;
+ m_fl.get_client_cfg_ip_list(conf);
+
+ // If we got src MAC for port in global config, take it, otherwise use src MAC from DPDK
+ uint8_t port_macs[m_max_ports][ETHER_ADDR_LEN];
+ for (int port_id = 0; port_id < m_max_ports; port_id++) {
+ memcpy(port_macs[port_id], CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src, ETHER_ADDR_LEN);
+ }
+
+ for (std::vector<ClientCfgCompactEntry *>::iterator it = conf.begin(); it != conf.end(); it++) {
+ uint8_t port = (*it)->get_port();
+ uint16_t vlan = (*it)->get_vlan();
+ uint32_t count = (*it)->get_count();
+ uint32_t dst_ip = (*it)->get_dst_ip();
+ uint32_t src_ip = (*it)->get_src_ip();
+
+ for (int i = 0; i < count; i++) {
+ //??? handle ipv6;
+ if ((*it)->is_ipv4()) {
+ pretest.add_next_hop(port, dst_ip + i, vlan);
+ }
+ }
+ if (!src_ip) {
+ src_ip = CGlobalInfo::m_options.m_ip_cfg[port].get_ip();
+ if (!src_ip) {
+ fprintf(stderr, "No matching src ip for port: %d ip:%s vlan: %d\n"
+ , port, ip_to_str(dst_ip).c_str(), vlan);
+ fprintf(stderr, "You must specify src_ip in client config file or in TRex config file\n");
+ exit(1);
+ }
+ }
+ pretest.add_ip(port, src_ip, vlan, port_macs[port]);
+ COneIPv4Info ipv4(src_ip, vlan, port_macs[port], port);
+ m_mg.add_grat_arp_src(ipv4);
+
+ delete *it;
+ }
+ if ( CGlobalInfo::m_options.preview.getVMode() > 1) {
+ fprintf(stdout, "*******Pretest for client cfg********\n");
+ pretest.dump(stdout);
+ }
+ } else {
+ for (int port_id = 0; port_id < m_max_ports; port_id++) {
+ if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) {
+ resolve_needed = true;
+ } else {
+ resolve_needed = false;
+ }
+
+ need_grat_arp[port_id] = CGlobalInfo::m_options.m_ip_cfg[port_id].get_ip() != 0;
+
+ pretest.add_ip(port_id, CGlobalInfo::m_options.m_ip_cfg[port_id].get_ip()
+ , CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan()
+ , CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src);
+
+ if (resolve_needed) {
+ pretest.add_next_hop(port_id, CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw()
+ , CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan());
+ }
+ }
+ }
for (int port_id = 0; port_id < m_max_ports; port_id++) {
CPhyEthIF *pif = &m_ports[port_id];
// Configure port to send all packets to software
CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, true);
- if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) {
- resolve_needed = true;
- } else {
- resolve_needed = false;
- }
- if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src, empty_mac, ETHER_ADDR_LEN)) {
- rte_eth_macaddr_get(port_id,
- (struct ether_addr *)&CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src);
- CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(true);
- } else {
- // If we got src MAC from config file, do not send gratuitous ARP for it (for compatibility with old behaviour)
- CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(false);
- }
- pretest.set_port_params(port_id, CGlobalInfo::m_options.m_ip_cfg[port_id]
- , CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src
- , resolve_needed);
}
+
pretest.send_grat_arp_all();
bool ret;
int count = 0;
+ bool resolve_failed = false;
do {
ret = pretest.resolve_all();
count++;
} while ((ret != true) && (count < 10));
+ if (ret != true) {
+ resolve_failed = true;
+ }
- if ( CGlobalInfo::m_options.preview.getVMode() > 0) {
+ if ( CGlobalInfo::m_options.preview.getVMode() > 1) {
+ fprintf(stdout, "*******Pretest after resolving ********\n");
pretest.dump(stdout);
}
- uint8_t mac[ETHER_ADDR_LEN];
- for (int port_id = 0; port_id < m_max_ports; port_id++) {
- if (! memcmp(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) {
- // we don't have dest MAC. Get it from what we resolved.
- uint32_t ip = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw();
- if (! pretest.get_mac(port_id, ip, mac)) {
- fprintf(stderr, "Failed resolving dest MAC for default gateway:%d.%d.%d.%d on port %d\n"
- , (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF, port_id);
- exit(1);
+
+ if (CGlobalInfo::m_options.preview.get_is_client_cfg_enable()) {
+ CManyIPInfo pretest_result;
+ pretest.get_results(pretest_result);
+ if (resolve_failed) {
+ fprintf(stderr, "Resolution of following IPs failed. Exiting.\n");
+ for (const COneIPInfo *ip=pretest_result.get_next(); ip != NULL;
+ ip = pretest_result.get_next()) {
+ if (ip->resolve_needed()) {
+ ip->dump(stderr, " ");
+ }
}
- memcpy(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, mac, ETHER_ADDR_LEN);
- // if port is connected in loopback, no need to send gratuitous ARP. It will only confuse our ingress counters.
- if (pretest.is_loopback(port_id))
- CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(false);
+ exit(1);
+ }
+ m_fl.set_client_config_resolved_macs(pretest_result);
+ if ( CGlobalInfo::m_options.preview.getVMode() > 1) {
+ m_fl.dump_client_config(stdout);
}
- // update statistics baseline, so we can ignore what happened in pre test phase
- CPhyEthIF *pif = &m_ports[port_id];
- CPreTestStats pre_stats = pretest.get_stats(port_id);
- pif->set_ignore_stats_base(pre_stats);
+ bool port_found[TREX_MAX_PORTS];
+ for (int port_id = 0; port_id < m_max_ports; port_id++) {
+ port_found[port_id] = false;
+ }
+ // If client config enabled, we don't resolve MACs from trex_cfg.yaml. For latency (-l)
+ // We need to able to send packets from RX core, so need to configure MAC/vlan for each port.
+ for (const COneIPInfo *ip=pretest_result.get_next(); ip != NULL; ip = pretest_result.get_next()) {
+ // Use first MAC/vlan we see on each port
+ uint8_t port_id = ip->get_port();
+ uint16_t vlan = ip->get_vlan();
+ if ( ! port_found[port_id]) {
+ port_found[port_id] = true;
+ ip->get_mac(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest);
+ CGlobalInfo::m_options.m_ip_cfg[port_id].set_vlan(vlan);
+ }
+ }
+ } else {
+ uint8_t mac[ETHER_ADDR_LEN];
+ for (int port_id = 0; port_id < m_max_ports; port_id++) {
+ if (! memcmp(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) {
+ // we don't have dest MAC. Get it from what we resolved.
+ uint32_t ip = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw();
+ uint16_t vlan = CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan();
+
+ if (!pretest.get_mac(port_id, ip, vlan, mac)) {
+ fprintf(stderr, "Failed resolving dest MAC for default gateway:%d.%d.%d.%d on port %d\n"
+ , (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF, port_id);
+
+ if (get_is_stateless()) {
+ continue;
+ } else {
+ exit(1);
+ }
+ }
- // Configure port back to normal mode. Only relevant packets handled by software.
- CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, false);
+
+
+ memcpy(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, mac, ETHER_ADDR_LEN);
+ // if port is connected in loopback, no need to send gratuitous ARP. It will only confuse our ingress counters.
+ if (need_grat_arp[port_id] && (! pretest.is_loopback(port_id))) {
+ COneIPv4Info ipv4(CGlobalInfo::m_options.m_ip_cfg[port_id].get_ip()
+ , CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan()
+ , CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src
+ , port_id);
+ m_mg.add_grat_arp_src(ipv4);
+ }
+ }
+
+ // update statistics baseline, so we can ignore what happened in pre test phase
+ CPhyEthIF *pif = &m_ports[port_id];
+ CPreTestStats pre_stats = pretest.get_stats(port_id);
+ pif->set_ignore_stats_base(pre_stats);
+
+ // Configure port back to normal mode. Only relevant packets handled by software.
+ CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, false);
+
+ }
+ }
+
+ /* for stateless only - set port mode */
+ if (get_is_stateless()) {
+ for (int port_id = 0; port_id < m_max_ports; port_id++) {
+ uint32_t src_ipv4 = CGlobalInfo::m_options.m_ip_cfg[port_id].get_ip();
+ uint32_t dg = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw();
+ const uint8_t *dst_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest;
+
+ /* L3 mode */
+ if (src_ipv4 && dg) {
+ if (memcmp(dst_mac, empty_mac, 6) == 0) {
+ m_trex_stateless->get_port_by_id(port_id)->set_l3_mode(src_ipv4, dg);
+ } else {
+ m_trex_stateless->get_port_by_id(port_id)->set_l3_mode(src_ipv4, dg, dst_mac);
+ }
+
+ /* L2 mode */
+ } else {
+ m_trex_stateless->get_port_by_id(port_id)->set_l2_mode(dst_mac);
+ }
+ }
}
+
+
}
/**
@@ -3191,12 +3426,14 @@ bool CGlobalTRex::is_all_links_are_up(bool dump){
void CGlobalTRex::try_stop_all_cores(){
TrexStatelessDpQuit * dp_msg= new TrexStatelessDpQuit();
- TrexStatelessRxQuit * rx_msg= new TrexStatelessRxQuit();
send_message_all_dp(dp_msg);
+ delete dp_msg;
+
if (get_is_stateless()) {
+ TrexStatelessRxQuit * rx_msg= new TrexStatelessRxQuit();
send_message_to_rx(rx_msg);
}
- delete dp_msg;
+
// no need to delete rx_msg. Deleted by receiver
bool all_core_finished = false;
int i;
@@ -3246,6 +3483,7 @@ int CGlobalTRex::ixgbe_rx_queue_flush(){
}
+// init stateful rx core
void CGlobalTRex::ixgbe_configure_mg(void) {
int i;
CLatencyManagerCfg mg_cfg;
@@ -3268,13 +3506,13 @@ void CGlobalTRex::ixgbe_configure_mg(void) {
if ( get_vm_one_queue_enable() ) {
/* vm mode, indirect queues */
for (i=0; i<m_max_ports; i++) {
-
+ CPhyEthIF * _if = &m_ports[i];
CMessagingManager * rx_dp=CMsgIns::Ins()->getRxDp();
uint8_t thread_id = (i>>1);
CNodeRing * r = rx_dp->getRingCpToDp(thread_id);
- m_latency_vm_vports[i].Create((uint8_t)i,r,&m_mg);
+ m_latency_vm_vports[i].Create((uint8_t)i, r, &m_mg, _if);
mg_cfg.m_ports[i] =&m_latency_vm_vports[i];
}
@@ -3283,7 +3521,7 @@ void CGlobalTRex::ixgbe_configure_mg(void) {
for (i=0; i<m_max_ports; i++) {
CPhyEthIF * _if=&m_ports[i];
_if->dump_stats(stdout);
- m_latency_vports[i].Create(_if,m_latency_tx_queue_id,1);
+ m_latency_vports[i].Create(_if, m_rx_core_tx_q_id, 1);
mg_cfg.m_ports[i] =&m_latency_vports[i];
}
@@ -3300,20 +3538,22 @@ void CGlobalTRex::rx_sl_configure(void) {
int i;
rx_sl_cfg.m_max_ports = m_max_ports;
+ rx_sl_cfg.m_num_crc_fix_bytes = get_ex_drv()->get_num_crc_fix_bytes();
if ( get_vm_one_queue_enable() ) {
/* vm mode, indirect queues */
for (i=0; i < m_max_ports; i++) {
+ CPhyEthIF * _if = &m_ports[i];
CMessagingManager * rx_dp = CMsgIns::Ins()->getRxDp();
uint8_t thread_id = (i >> 1);
CNodeRing * r = rx_dp->getRingCpToDp(thread_id);
- m_latency_vm_vports[i].Create((uint8_t)i, r, &m_mg);
+ m_latency_vm_vports[i].Create(i, r, &m_mg, _if);
rx_sl_cfg.m_ports[i] = &m_latency_vm_vports[i];
}
} else {
for (i = 0; i < m_max_ports; i++) {
CPhyEthIF * _if = &m_ports[i];
- m_latency_vports[i].Create(_if, m_latency_tx_queue_id, 1);
+ m_latency_vports[i].Create(_if, m_rx_core_tx_q_id, 1);
rx_sl_cfg.m_ports[i] = &m_latency_vports[i];
}
}
@@ -3324,88 +3564,85 @@ void CGlobalTRex::rx_sl_configure(void) {
int CGlobalTRex::ixgbe_start(void){
int i;
for (i=0; i<m_max_ports; i++) {
-
+ socket_id_t socket_id = CGlobalInfo::m_socket.port_to_socket((port_id_t)i);
+ assert(CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048);
CPhyEthIF * _if=&m_ports[i];
_if->Create((uint8_t)i);
- /* last TX queue if for latency check */
- if ( get_vm_one_queue_enable() ) {
- /* one tx one rx */
+ uint16_t rx_rss = get_ex_drv()->enable_rss_drop_workaround();
+ if ( get_vm_one_queue_enable() ) {
/* VMXNET3 does claim to support 16K but somehow does not work */
/* reduce to 2000 */
m_port_cfg.m_port_conf.rxmode.max_rx_pkt_len = 2000;
-
- _if->configure(1,
- 1,
- &m_port_cfg.m_port_conf);
-
- /* will not be used */
- m_latency_tx_queue_id= m_cores_to_dual_ports;
-
- socket_id_t socket_id = CGlobalInfo::m_socket.port_to_socket((port_id_t)i);
- assert(CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048);
-
-
-
+ /* In VM case, there is one tx q and one rx q */
+ _if->configure(1, 1, &m_port_cfg.m_port_conf);
+ // Only 1 rx queue, so use it for everything
+ m_rx_core_tx_q_id = 0;
_if->set_rx_queue(0);
- _if->rx_queue_setup(0,
- RTE_TEST_RX_DESC_VM_DEFAULT,
- socket_id,
- &m_port_cfg.m_rx_conf,
+ _if->rx_queue_setup(0, RTE_TEST_RX_DESC_VM_DEFAULT, socket_id, &m_port_cfg.m_rx_conf,
CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048);
+ // 1 TX queue in VM case
+ _if->tx_queue_setup(0, RTE_TEST_TX_DESC_VM_DEFAULT, socket_id, &m_port_cfg.m_tx_conf);
+ } else {
+ // 2 rx queues.
+ // TX queues: 1 for each core handling the port pair + 1 for latency pkts + 1 for use by RX core
+
+ uint16_t rx_queues;
- int qid;
- for ( qid=0; qid<(m_max_queues_per_port); qid++) {
- _if->tx_queue_setup((uint16_t)qid,
- RTE_TEST_TX_DESC_VM_DEFAULT ,
- socket_id,
- &m_port_cfg.m_tx_conf);
-
+ if (rx_rss==0) {
+ rx_queues=2;
+ }else{
+ rx_queues=rx_rss;
}
- }else{
- _if->configure(2,
- m_cores_to_dual_ports+1,
- &m_port_cfg.m_port_conf);
-
- /* the latency queue for latency measurement packets */
- m_latency_tx_queue_id= m_cores_to_dual_ports;
+ _if->configure(rx_queues, m_cores_to_dual_ports + 2, &m_port_cfg.m_port_conf);
+ m_rx_core_tx_q_id = m_cores_to_dual_ports;
- socket_id_t socket_id = CGlobalInfo::m_socket.port_to_socket((port_id_t)i);
- assert(CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048);
+ if ( rx_rss ) {
+ int j=0;
+ for (j=0;j<rx_rss; j++) {
+ if (j==MAIN_DPDK_RX_Q){
+ continue;
+ }
+ /* drop queue */
+ _if->rx_queue_setup(j,
+ RTE_TEST_RX_DESC_DEFAULT_MLX,
+ socket_id,
+ &m_port_cfg.m_rx_conf,
+ CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048);
- /* drop queue */
- _if->rx_queue_setup(0,
- RTE_TEST_RX_DESC_DEFAULT,
- socket_id,
- &m_port_cfg.m_rx_conf,
- CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048);
-
+ }
+ }else{
+ // setup RX drop queue
+ _if->rx_queue_setup(MAIN_DPDK_DATA_Q,
+ RTE_TEST_RX_DESC_DEFAULT,
+ socket_id,
+ &m_port_cfg.m_rx_conf,
+ CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_2048);
+ // setup RX filter queue
+ _if->set_rx_queue(MAIN_DPDK_RX_Q);
+ }
- /* set the filter queue */
- _if->set_rx_queue(1);
- /* latency measurement ring is 1 */
- _if->rx_queue_setup(1,
+ _if->rx_queue_setup(MAIN_DPDK_RX_Q,
RTE_TEST_RX_LATENCY_DESC_DEFAULT,
socket_id,
&m_port_cfg.m_rx_conf,
CGlobalInfo::m_mem_pool[socket_id].m_mbuf_pool_9k);
- int qid;
- for ( qid=0; qid<(m_max_queues_per_port+1); qid++) {
+ for (int qid = 0; qid < m_max_queues_per_port; qid++) {
_if->tx_queue_setup((uint16_t)qid,
RTE_TEST_TX_DESC_DEFAULT ,
socket_id,
&m_port_cfg.m_tx_conf);
-
}
-
}
+ if ( rx_rss ){
+ _if->configure_rss_redirect_table(rx_rss,MAIN_DPDK_RX_Q);
+ }
_if->stats_clear();
-
_if->start();
_if->configure_rx_duplicate_rules();
@@ -3430,8 +3667,7 @@ int CGlobalTRex::ixgbe_start(void){
printf(" WARNING : there is no link on one of the ports, driver support auto drop in case of link down - continue\n");
}else{
dump_links_status(stdout);
- rte_exit(EXIT_FAILURE, " "
- " one of the link is down \n");
+ rte_exit(EXIT_FAILURE, " One of the links is down \n");
}
}
} else {
@@ -3444,9 +3680,7 @@ int CGlobalTRex::ixgbe_start(void){
if (! get_is_stateless()) {
ixgbe_configure_mg();
- } else {
- rx_sl_configure();
- }
+ }
/* core 0 - control
@@ -3462,7 +3696,7 @@ int CGlobalTRex::ixgbe_start(void){
if ( get_vm_one_queue_enable() ) {
lat_q_id = 0;
} else {
- lat_q_id = get_cores_tx() / get_base_num_cores();
+ lat_q_id = get_cores_tx() / get_base_num_cores() + 1;
}
for (i=0; i<get_cores_tx(); i++) {
int j=(i+1);
@@ -3487,6 +3721,7 @@ int CGlobalTRex::ixgbe_start(void){
}
fprintf(stdout," -------------------------------\n");
+ fprintf(stdout, "RX core uses TX queue number %d on all ports\n", m_rx_core_tx_q_id);
CCoreEthIF::DumpIfCfgHeader(stdout);
for (i=0; i<get_cores_tx(); i++) {
m_cores_vif[i+1]->DumpIfCfg(stdout);
@@ -3584,13 +3819,22 @@ bool CGlobalTRex::Create(){
cfg.m_publisher = &m_zmq_publisher;
m_trex_stateless = new TrexStateless(cfg);
+
+ rx_sl_configure();
}
return (true);
}
void CGlobalTRex::Delete(){
+
m_zmq_publisher.Delete();
+ m_fl.Delete();
+
+ if (m_trex_stateless) {
+ delete m_trex_stateless;
+ m_trex_stateless = NULL;
+ }
}
@@ -3713,12 +3957,12 @@ int CGlobalTRex::queues_prob_init(){
m_cores_to_dual_ports = 2;
*/
- /* number of queue - 1 per core for dual ports*/
- m_max_queues_per_port = m_cores_to_dual_ports;
+ // One q for each core allowed to send on this port + 1 for latency q (Used in stateless) + 1 for RX core.
+ m_max_queues_per_port = m_cores_to_dual_ports + 2;
if (m_max_queues_per_port > BP_MAX_TX_QUEUE) {
rte_exit(EXIT_FAILURE,
- "maximum number of queue should be maximum %d \n",BP_MAX_TX_QUEUE);
+ "Error: Number of TX queues exceeds %d. Try running with lower -c <val> \n",BP_MAX_TX_QUEUE);
}
assert(m_max_queues_per_port>0);
@@ -3740,6 +3984,16 @@ void CGlobalTRex::dump_links_status(FILE *fd){
}
}
+bool CGlobalTRex::lookup_port_by_mac(const uint8_t *mac, uint8_t &port_id) {
+ for (int i = 0; i < m_max_ports; i++) {
+ if (memcmp(m_ports[i].get_port_attr()->get_src_mac(), mac, 6) == 0) {
+ port_id = i;
+ return true;
+ }
+ }
+
+ return false;
+}
void CGlobalTRex::dump_post_test_stats(FILE *fd){
uint64_t pkt_out=0;
@@ -3789,6 +4043,11 @@ void CGlobalTRex::dump_post_test_stats(FILE *fd){
}
else
fprintf (fd, " Total-pkt-drop : %llu pkts \n", (unsigned long long) (pkt_out - pkt_in));
+ for (i=0; i<m_max_ports; i++) {
+ if ( m_stats.m_port[i].m_link_was_down ) {
+ fprintf (fd, " WARNING: Link was down at port %d during test (at least for some time)!\n", i);
+ }
+ }
fprintf (fd," Total-tx-bytes : %llu bytes \n", (unsigned long long)pkt_out_bytes);
fprintf (fd," Total-tx-sw-bytes : %llu bytes \n", (unsigned long long)sw_pkt_out_bytes);
fprintf (fd," Total-rx-bytes : %llu byte \n", (unsigned long long)pkt_in_bytes);
@@ -3904,6 +4163,8 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){
stp->m_total_tx_pps = _if->get_last_tx_pps_rate();
stp->m_total_rx_bps = _if->get_last_rx_rate()*_1Mb_DOUBLE;
stp->m_total_rx_pps = _if->get_last_rx_pps_rate();
+ stp->m_link_up = _if->get_port_attr()->is_link_up();
+ stp->m_link_was_down |= ! _if->get_port_attr()->is_link_up();
stats.m_total_tx_pkts += st.opackets;
stats.m_total_rx_pkts += st.ipackets;
@@ -4189,16 +4450,7 @@ CGlobalTRex:: publish_async_port_attr_changed(uint8_t port_id) {
data["port_id"] = port_id;
TRexPortAttr * _attr = m_ports[port_id].get_port_attr();
- /* attributes */
- data["attr"]["speed"] = _attr->get_link_speed();
- data["attr"]["promiscuous"]["enabled"] = _attr->get_promiscuous();
- data["attr"]["link"]["up"] = _attr->is_link_up();
- int mode;
- int ret = _attr->get_flow_ctrl(mode);
- if (ret != 0) {
- mode = -1;
- }
- data["attr"]["fc"]["mode"] = mode;
+ _attr->to_json(data["attr"]);
m_zmq_publisher.publish_event(TrexPublisher::EVENT_PORT_ATTR_CHANGED, data);
}
@@ -4270,18 +4522,20 @@ CGlobalTRex::handle_slow_path() {
m_mg.update();
if ( m_io_modes.m_g_mode == CTrexGlobalIoMode::gNORMAL ) {
- switch (m_io_modes.m_l_mode) {
- case CTrexGlobalIoMode::lDISABLE:
- fprintf(stdout,"\n+Latency stats disabled \n");
- break;
- case CTrexGlobalIoMode::lENABLE:
- fprintf(stdout,"\n-Latency stats enabled \n");
- m_mg.DumpShort(stdout);
- break;
- case CTrexGlobalIoMode::lENABLE_Extended:
- fprintf(stdout,"\n-Latency stats extended \n");
- m_mg.Dump(stdout);
- break;
+ if (CGlobalInfo::m_options.m_latency_rate != 0) {
+ switch (m_io_modes.m_l_mode) {
+ case CTrexGlobalIoMode::lDISABLE:
+ fprintf(stdout, "\n+Latency stats disabled \n");
+ break;
+ case CTrexGlobalIoMode::lENABLE:
+ fprintf(stdout, "\n-Latency stats enabled \n");
+ m_mg.DumpShort(stdout);
+ break;
+ case CTrexGlobalIoMode::lENABLE_Extended:
+ fprintf(stdout, "\n-Latency stats extended \n");
+ m_mg.Dump(stdout);
+ break;
+ }
}
if ( get_is_rx_check_mode() ) {
@@ -4400,8 +4654,11 @@ void CGlobalTRex::shutdown() {
for (int i = 0; i < m_max_ports; i++) {
m_ports[i].stop();
}
+
if (m_mark_for_shutdown != SHUTDOWN_TEST_ENDED) {
/* we should stop latency and exit to stop agents */
+ Delete();
+ utl_termio_reset();
exit(-1);
}
}
@@ -4565,7 +4822,6 @@ int CGlobalTRex::stop_master(){
dump_stats(stdout,CGlobalStats::dmpSTANDARD);
dump_post_test_stats(stdout);
- m_fl.Delete();
return (0);
}
@@ -4626,6 +4882,8 @@ int CGlobalTRex::start_master_statefull() {
exit(-1);
}
CGlobalInfo::m_options.preview.set_client_cfg_enable(true);
+ m_fl.set_client_config_tuple_gen_info(&m_fl.m_yaml_info.m_tuple_gen);
+ pre_test();
}
/* verify options */
@@ -4680,10 +4938,67 @@ int CGlobalTRex::start_master_statefull() {
////////////////////////////////////////////
static CGlobalTRex g_trex;
+
+void CPhyEthIF::configure_rss_redirect_table(uint16_t numer_of_queues,
+ uint16_t skip_queue){
+
+
+ struct rte_eth_dev_info dev_info;
+
+ rte_eth_dev_info_get(m_port_id,&dev_info);
+ assert(dev_info.reta_size>0);
+
+ int reta_conf_size =
+ std::max(1, dev_info.reta_size / RTE_RETA_GROUP_SIZE);
+
+ struct rte_eth_rss_reta_entry64 reta_conf[reta_conf_size];
+
+ rte_eth_dev_rss_reta_query(m_port_id,&reta_conf[0],dev_info.reta_size);
+
+ int i,j;
+
+ for (j=0; j<reta_conf_size; j++) {
+ uint16_t skip=0;
+ reta_conf[j].mask = ~0ULL;
+ for (i=0; i<RTE_RETA_GROUP_SIZE; i++) {
+ uint16_t q;
+ while (true) {
+ q=(i+skip)%numer_of_queues;
+ if (q!=skip_queue) {
+ break;
+ }
+ skip+=1;
+ }
+ reta_conf[j].reta[i]=q;
+ // printf(" %d %d %d \n",j,i,q);
+ }
+ }
+ rte_eth_dev_rss_reta_update(m_port_id,&reta_conf[0],dev_info.reta_size);
+
+ rte_eth_dev_rss_reta_query(m_port_id,&reta_conf[0],dev_info.reta_size);
+
+ #if 0
+ /* verification */
+ for (j=0; j<reta_conf_size; j++) {
+ for (i=0; i<RTE_RETA_GROUP_SIZE; i++) {
+ printf(" R %d %d %d \n",j,i,reta_conf[j].reta[i]);
+ }
+ }
+ #endif
+
+}
+
+
void CPhyEthIF::update_counters() {
get_ex_drv()->get_extended_stats(this, &m_stats);
CRXCoreIgnoreStat ign_stats;
- g_trex.m_mg.get_ignore_stats(m_port_id, ign_stats, true);
+
+ if (get_is_stateless()) {
+ g_trex.m_rx_sl.get_ignore_stats(m_port_id, ign_stats, true);
+ } else {
+ g_trex.m_mg.get_ignore_stats(m_port_id, ign_stats, true);
+ }
+
m_stats.obytes -= ign_stats.get_tx_bytes();
m_stats.opackets -= ign_stats.get_tx_pkts();
m_ignore_stats.opackets += ign_stats.get_tx_pkts();
@@ -4703,6 +5018,13 @@ bool CPhyEthIF::Create(uint8_t portid) {
m_last_tx_pps = 0.0;
m_port_attr = g_trex.m_drv->create_port_attr(portid);
+ /* set src MAC addr */
+ uint8_t empty_mac[ETHER_ADDR_LEN] = {0,0,0,0,0,0};
+ if (! memcmp( CGlobalInfo::m_options.m_mac_addr[m_port_id].u.m_mac.src, empty_mac, ETHER_ADDR_LEN)) {
+ rte_eth_macaddr_get(m_port_id,
+ (struct ether_addr *)&CGlobalInfo::m_options.m_mac_addr[m_port_id].u.m_mac.src);
+ }
+
return true;
}
@@ -5355,7 +5677,10 @@ int main_test(int argc , char * argv[]){
}
}
- g_trex.pre_test();
+ // in case of client config, we already run pretest
+ if (! CGlobalInfo::m_options.preview.get_is_client_cfg_enable()) {
+ g_trex.pre_test();
+ }
// after doing all needed ARP resolution, we need to flush queues, and stop our drop queue
g_trex.ixgbe_rx_queue_flush();
@@ -5779,6 +6104,7 @@ void CTRexExtendedDriverBase10G::update_configuration(port_cfg_t * cfg){
}
int CTRexExtendedDriverBase10G::configure_rx_filter_rules(CPhyEthIF * _if) {
+ set_rcv_all(_if, false);
if ( get_is_stateless() ) {
return configure_rx_filter_rules_stateless(_if);
} else {
@@ -5809,7 +6135,7 @@ int CTRexExtendedDriverBase10G::configure_rx_filter_rules_stateless(CPhyEthIF *
res = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_ADD, &fdir_filter);
if (res != 0) {
- rte_exit(EXIT_FAILURE, " ERROR rte_eth_dev_filter_ctrl : %d\n",res);
+ rte_exit(EXIT_FAILURE, "Error: rte_eth_dev_filter_ctrl in configure_rx_filter_rules_stateless: %d\n",res);
}
}
@@ -5878,12 +6204,51 @@ int CTRexExtendedDriverBase10G::configure_rx_filter_rules_statefull(CPhyEthIF *
res = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_ADD, &fdir_filter);
if (res != 0) {
- rte_exit(EXIT_FAILURE, " ERROR rte_eth_dev_filter_ctrl : %d\n",res);
+ rte_exit(EXIT_FAILURE, "Error: rte_eth_dev_filter_ctrl in configure_rx_filter_rules_statefull: %d\n",res);
}
}
return (0);
}
+int CTRexExtendedDriverBase10G::add_del_eth_filter(CPhyEthIF * _if, bool is_add, uint16_t ethertype) {
+ int res = 0;
+ uint8_t port_id=_if->get_rte_port_id();
+ struct rte_eth_ethertype_filter filter;
+ enum rte_filter_op op;
+
+ memset(&filter, 0, sizeof(filter));
+ filter.ether_type = ethertype;
+ res = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_ETHERTYPE, RTE_ETH_FILTER_GET, &filter);
+
+ if (is_add && (res >= 0))
+ return 0;
+ if ((! is_add) && (res == -ENOENT))
+ return 0;
+
+ if (is_add) {
+ op = RTE_ETH_FILTER_ADD;
+ } else {
+ op = RTE_ETH_FILTER_DELETE;
+ }
+
+ filter.queue = 1;
+ res = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_ETHERTYPE, op, &filter);
+ if (res != 0) {
+ printf("Error: %s L2 filter for ethertype 0x%04x returned %d\n", is_add ? "Adding":"Deleting", ethertype, res);
+ exit(1);
+ }
+ return 0;
+}
+
+int CTRexExtendedDriverBase10G::set_rcv_all(CPhyEthIF * _if, bool set_on) {
+ int res = 0;
+ res = add_del_eth_filter(_if, set_on, ETHER_TYPE_ARP);
+ res |= add_del_eth_filter(_if, set_on, ETHER_TYPE_IPv4);
+ res |= add_del_eth_filter(_if, set_on, ETHER_TYPE_IPv6);
+
+ return res;
+}
+
void CTRexExtendedDriverBase10G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){
int i;
@@ -5937,12 +6302,6 @@ void CTRexExtendedDriverBase40G::clear_extended_stats(CPhyEthIF * _if){
rte_eth_stats_reset(_if->get_port_id());
}
-void CTRexExtendedDriverBaseVIC::update_configuration(port_cfg_t * cfg){
- cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH;
- cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH;
- cfg->m_tx_conf.tx_thresh.wthresh = TX_WTHRESH;
- cfg->m_port_conf.rxmode.max_rx_pkt_len =9*1000-10;
-}
void CTRexExtendedDriverBase40G::update_configuration(port_cfg_t * cfg){
cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH;
@@ -5954,7 +6313,7 @@ void CTRexExtendedDriverBase40G::update_configuration(port_cfg_t * cfg){
// What is the type of the rule the respective hw_id counter counts.
struct fdir_hw_id_params_t {
uint16_t rule_type;
- uint16_t l4_proto;
+ uint8_t l4_proto;
};
static struct fdir_hw_id_params_t fdir_hw_id_rule_params[512];
@@ -5963,7 +6322,7 @@ static struct fdir_hw_id_params_t fdir_hw_id_rule_params[512];
// ttl is used in statefull mode, and ip_id in stateless. We configure the driver registers so that only one of them applies.
// So, the rule will apply if packet has either the correct ttl or IP ID, depending if we are in statfull or stateless.
void CTRexExtendedDriverBase40G::add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, uint8_t ttl
- , uint16_t ip_id, uint16_t l4_proto, int queue, uint16_t stat_idx) {
+ , uint16_t ip_id, uint8_t l4_proto, int queue, uint16_t stat_idx) {
int ret=rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_FDIR);
static int filter_soft_id = 0;
@@ -6114,11 +6473,11 @@ int CTRexExtendedDriverBase40G::configure_rx_filter_rules(CPhyEthIF * _if) {
add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, 0
, FLOW_STAT_PAYLOAD_IP_ID, IPPROTO_ICMP, MAIN_DPDK_RX_Q, FDIR_PAYLOAD_RULES_HW_ID);
add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, 0
- , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, 0);
+ , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, FDIR_PAYLOAD_RULES_HW_ID);
add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_TCP, 0
- , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, 0);
+ , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, FDIR_PAYLOAD_RULES_HW_ID);
add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, 0
- , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, 0);
+ , FLOW_STAT_PAYLOAD_IP_ID, 0, MAIN_DPDK_RX_Q, FDIR_PAYLOAD_RULES_HW_ID);
rte_eth_fdir_stats_reset(_if->get_port_id(), NULL, FDIR_TEMP_HW_ID, 1);
return 0; // Other rules are configured dynamically in stateless
@@ -6301,6 +6660,405 @@ int CTRexExtendedDriverBase40G::set_rcv_all(CPhyEthIF * _if, bool set_on) {
}
/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+/* MLX5 */
+
+void CTRexExtendedDriverBaseMlnx5G::clear_extended_stats(CPhyEthIF * _if){
+ rte_eth_stats_reset(_if->get_port_id());
+}
+
+void CTRexExtendedDriverBaseMlnx5G::update_configuration(port_cfg_t * cfg){
+ cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH;
+ cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH;
+ cfg->m_tx_conf.tx_thresh.wthresh = TX_WTHRESH;
+ cfg->update_global_config_fdir_40g();
+ /* update mask */
+ cfg->m_port_conf.fdir_conf.mask.ipv4_mask.proto=0xff;
+ cfg->m_port_conf.fdir_conf.mask.ipv4_mask.tos=0x01;
+ cfg->m_port_conf.fdir_conf.mask.ipv6_mask.proto=0xff;
+ cfg->m_port_conf.fdir_conf.mask.ipv6_mask.tc=0x01;
+
+ /* enable RSS */
+ cfg->m_port_conf.rxmode.mq_mode =ETH_MQ_RX_RSS;
+ cfg->m_port_conf.rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IP;
+
+}
+
+/*
+ In case of MLX5 driver, the rule is not really added according to givern parameters.
+ ip_id == 1 means add rule on TOS (or traffic_class) field.
+ ip_id == 2 means add rule to receive all packets.
+ */
+void CTRexExtendedDriverBaseMlnx5G::add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type,
+ uint16_t ip_id, uint8_t l4_proto, int queue) {
+ int ret = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_FDIR);
+ static int filter_soft_id = 0;
+
+ if ( ret != 0 ) {
+ rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_supported err=%d, port=%u \n", ret, port_id);
+ }
+
+ struct rte_eth_fdir_filter filter;
+
+ memset(&filter,0,sizeof(struct rte_eth_fdir_filter));
+
+#if 0
+ printf("MLNX add_del_rules::%s rules: port:%d type:%d ip_id:%x l4:%d q:%d\n"
+ , (op == RTE_ETH_FILTER_ADD) ? "add" : "del"
+ , port_id, type, ip_id, l4_proto, queue);
+#endif
+
+ filter.action.rx_queue = queue;
+ filter.action.behavior = RTE_ETH_FDIR_ACCEPT;
+ filter.action.report_status = RTE_ETH_FDIR_NO_REPORT_STATUS;
+ filter.soft_id = filter_soft_id++;
+ filter.input.flow_type = type;
+
+ switch (type) {
+ case RTE_ETH_FLOW_NONFRAG_IPV4_UDP:
+ case RTE_ETH_FLOW_NONFRAG_IPV4_TCP:
+ case RTE_ETH_FLOW_NONFRAG_IPV4_SCTP:
+ case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER:
+ filter.input.flow.ip4_flow.ip_id = ip_id;
+ if (l4_proto != 0)
+ filter.input.flow.ip4_flow.proto = l4_proto;
+ break;
+ case RTE_ETH_FLOW_NONFRAG_IPV6_UDP:
+ case RTE_ETH_FLOW_NONFRAG_IPV6_TCP:
+ case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER:
+ filter.input.flow.ipv6_flow.flow_label = ip_id;
+ filter.input.flow.ipv6_flow.proto = l4_proto;
+ break;
+ }
+
+ ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, op, (void*)&filter);
+ if ( ret != 0 ) {
+ if (((op == RTE_ETH_FILTER_ADD) && (ret == EEXIST)) || ((op == RTE_ETH_FILTER_DELETE) && (ret == ENOENT)))
+ return;
+
+ rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_ctrl: err=%d, port=%u\n",
+ ret, port_id);
+ }
+}
+
+int CTRexExtendedDriverBaseMlnx5G::set_rcv_all(CPhyEthIF * _if, bool set_on) {
+ uint8_t port_id=_if->get_rte_port_id();
+
+ if (set_on) {
+ add_del_rx_filter_rules(_if, false);
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, 2, 17, MAIN_DPDK_RX_Q);
+ } else {
+ add_del_rules(RTE_ETH_FILTER_DELETE, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, 2, 17, MAIN_DPDK_RX_Q);
+ add_del_rx_filter_rules(_if, true);
+ }
+
+ return 0;
+
+}
+
+int CTRexExtendedDriverBaseMlnx5G::add_del_rx_filter_rules(CPhyEthIF * _if, bool set_on) {
+ uint32_t port_id = _if->get_port_id();
+ enum rte_filter_op op;
+
+ if (set_on) {
+ op = RTE_ETH_FILTER_ADD;
+ } else {
+ op = RTE_ETH_FILTER_DELETE;
+ }
+
+ add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, 1, 17, MAIN_DPDK_RX_Q);
+ add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, 1, 6, MAIN_DPDK_RX_Q);
+ add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, 1, 1, MAIN_DPDK_RX_Q); /*ICMP*/
+ add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, 1, 132, MAIN_DPDK_RX_Q); /*SCTP*/
+ add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, 1, 17, MAIN_DPDK_RX_Q);
+ add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_TCP, 1, 6, MAIN_DPDK_RX_Q);
+ add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, 1, 1, MAIN_DPDK_RX_Q); /*ICMP*/
+ add_del_rules(op, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, 1, 132, MAIN_DPDK_RX_Q); /*SCTP*/
+
+ return 0;
+}
+
+int CTRexExtendedDriverBaseMlnx5G::configure_rx_filter_rules(CPhyEthIF * _if) {
+ set_rcv_all(_if, false);
+ return add_del_rx_filter_rules(_if, true);
+}
+
+void CTRexExtendedDriverBaseMlnx5G::reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len) {
+ for (int i =0; i < len; i++) {
+ stats[i] = 0;
+ }
+}
+
+int CTRexExtendedDriverBaseMlnx5G::get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts
+ ,uint32_t *bytes, uint32_t *prev_bytes, int min, int max) {
+ /* not supported yet */
+ return 0;
+}
+
+int CTRexExtendedDriverBaseMlnx5G::dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd)
+{
+ uint32_t port_id = _if->get_port_id();
+ struct rte_eth_fdir_stats stat;
+ int ret;
+
+ ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_STATS, (void*)&stat);
+ if (ret == 0) {
+ if (fd)
+ fprintf(fd, "Num filters on guarant poll:%d, best effort poll:%d\n", stat.guarant_cnt, stat.best_cnt);
+ return (stat.guarant_cnt + stat.best_cnt);
+ } else {
+ if (fd)
+ fprintf(fd, "Failed reading fdir statistics\n");
+ return -1;
+ }
+}
+
+void CTRexExtendedDriverBaseMlnx5G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){
+
+ struct rte_eth_stats stats1;
+ struct rte_eth_stats *prev_stats = &stats->m_prev_stats;
+ rte_eth_stats_get(_if->get_port_id(), &stats1);
+
+ stats->ipackets += stats1.ipackets - prev_stats->ipackets;
+ stats->ibytes += stats1.ibytes - prev_stats->ibytes +
+ + (stats1.ipackets << 2) - (prev_stats->ipackets << 2);
+ stats->opackets += stats1.opackets - prev_stats->opackets;
+ stats->obytes += stats1.obytes - prev_stats->obytes
+ + (stats1.opackets << 2) - (prev_stats->opackets << 2);
+ stats->f_ipackets += 0;
+ stats->f_ibytes += 0;
+ stats->ierrors += stats1.imissed + stats1.ierrors + stats1.rx_nombuf
+ - prev_stats->imissed - prev_stats->ierrors - prev_stats->rx_nombuf;
+ stats->oerrors += stats1.oerrors - prev_stats->oerrors;
+ stats->imcasts += 0;
+ stats->rx_nombuf += stats1.rx_nombuf - prev_stats->rx_nombuf;
+
+ prev_stats->ipackets = stats1.ipackets;
+ prev_stats->ibytes = stats1.ibytes;
+ prev_stats->opackets = stats1.opackets;
+ prev_stats->obytes = stats1.obytes;
+ prev_stats->imissed = stats1.imissed;
+ prev_stats->oerrors = stats1.oerrors;
+ prev_stats->ierrors = stats1.ierrors;
+ prev_stats->rx_nombuf = stats1.rx_nombuf;
+}
+
+int CTRexExtendedDriverBaseMlnx5G::wait_for_stable_link(){
+ delay(20);
+ return (0);
+}
+
+CFlowStatParser *CTRexExtendedDriverBaseMlnx5G::get_flow_stat_parser() {
+ CFlowStatParser *parser = new CFlowStatParser();
+ assert (parser);
+ return parser;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+/* VIC */
+
+void CTRexExtendedDriverBaseVIC::update_configuration(port_cfg_t * cfg){
+ cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH;
+ cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH;
+ cfg->m_tx_conf.tx_thresh.wthresh = TX_WTHRESH;
+ cfg->m_port_conf.rxmode.max_rx_pkt_len =9*1000-10;
+ cfg->m_port_conf.fdir_conf.mask.ipv4_mask.tos = 0x01;
+ cfg->m_port_conf.fdir_conf.mask.ipv6_mask.tc = 0x01;
+}
+
+void CTRexExtendedDriverBaseVIC::add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type
+ , uint16_t id, uint8_t l4_proto, uint8_t tos, int queue) {
+ int ret=rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_FDIR);
+
+ if ( ret != 0 ){
+ rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_supported "
+ "err=%d, port=%u \n",
+ ret, port_id);
+ }
+
+ struct rte_eth_fdir_filter filter;
+
+ memset(&filter,0,sizeof(struct rte_eth_fdir_filter));
+
+#if 0
+ printf("VIC add_del_rules::%s rules: port:%d type:%d id:%d l4:%d tod:%d, q:%d\n"
+ , (op == RTE_ETH_FILTER_ADD) ? "add" : "del"
+ , port_id, type, id, l4_proto, tos, queue);
+#endif
+
+ filter.action.rx_queue = queue;
+ filter.action.behavior = RTE_ETH_FDIR_ACCEPT;
+ filter.action.report_status = RTE_ETH_FDIR_NO_REPORT_STATUS;
+ filter.soft_id = id;
+ filter.input.flow_type = type;
+
+ switch (type) {
+ case RTE_ETH_FLOW_NONFRAG_IPV4_UDP:
+ case RTE_ETH_FLOW_NONFRAG_IPV4_TCP:
+ case RTE_ETH_FLOW_NONFRAG_IPV4_SCTP:
+ case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER:
+ filter.input.flow.ip4_flow.tos = tos;
+ filter.input.flow.ip4_flow.proto = l4_proto;
+ break;
+ case RTE_ETH_FLOW_NONFRAG_IPV6_UDP:
+ case RTE_ETH_FLOW_NONFRAG_IPV6_TCP:
+ case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER:
+ filter.input.flow.ipv6_flow.tc = tos;
+ filter.input.flow.ipv6_flow.proto = l4_proto;
+ break;
+ }
+
+ ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, op, (void*)&filter);
+ if ( ret != 0 ) {
+ if (((op == RTE_ETH_FILTER_ADD) && (ret == -EEXIST)) || ((op == RTE_ETH_FILTER_DELETE) && (ret == -ENOENT)))
+ return;
+
+ rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_ctrl: err=%d, port=%u\n",
+ ret, port_id);
+ }
+}
+
+int CTRexExtendedDriverBaseVIC::add_del_eth_type_rule(uint8_t port_id, enum rte_filter_op op, uint16_t eth_type) {
+ int ret;
+ struct rte_eth_ethertype_filter filter;
+
+ memset(&filter, 0, sizeof(filter));
+ filter.ether_type = eth_type;
+ filter.flags = 0;
+ filter.queue = MAIN_DPDK_RX_Q;
+ ret = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_ETHERTYPE, op, (void *) &filter);
+
+ return ret;
+}
+
+int CTRexExtendedDriverBaseVIC::configure_rx_filter_rules_statefull(CPhyEthIF * _if) {
+ uint32_t port_id = _if->get_port_id();
+
+ set_rcv_all(_if, false);
+
+ // Rules to direct all IP packets with tos lsb bit 1 to RX Q.
+ // IPv4
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, 1, 17, 0x1, MAIN_DPDK_RX_Q);
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, 1, 6, 0x1, MAIN_DPDK_RX_Q);
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_SCTP, 1, 132, 0x1, MAIN_DPDK_RX_Q); /*SCTP*/
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, 1, 1, 0x1, MAIN_DPDK_RX_Q); /*ICMP*/
+ // Ipv6
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, 1, 6, 0x1, MAIN_DPDK_RX_Q);
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, 1, 17, 0x1, MAIN_DPDK_RX_Q);
+
+ // Because of some issue with VIC firmware, IPv6 UDP and ICMP go by default to q 1, so we
+ // need these rules to make them go to q 0.
+ // rule appply to all packets with 0 on tos lsb.
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_OTHER, 1, 6, 0, MAIN_DPDK_DATA_Q);
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV6_UDP, 1, 17, 0, MAIN_DPDK_DATA_Q);
+
+ return 0;
+}
+
+
+int CTRexExtendedDriverBaseVIC::set_rcv_all(CPhyEthIF * _if, bool set_on) {
+ uint8_t port_id = _if->get_rte_port_id();
+
+ // soft ID 100 tells VIC driver to add rule for all ether types.
+ // Added with highest priority (implicitly in the driver), so if it exists, it applies before all other rules
+ if (set_on) {
+ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, 100, 30, 0, MAIN_DPDK_RX_Q);
+ } else {
+ add_del_rules(RTE_ETH_FILTER_DELETE, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, 100, 30, 0, MAIN_DPDK_RX_Q);
+ }
+
+ return 0;
+
+}
+
+void CTRexExtendedDriverBaseVIC::clear_extended_stats(CPhyEthIF * _if){
+ rte_eth_stats_reset(_if->get_port_id());
+}
+
+void CTRexExtendedDriverBaseVIC::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats) {
+ struct rte_eth_stats stats1;
+ struct rte_eth_stats *prev_stats = &stats->m_prev_stats;
+ rte_eth_stats_get(_if->get_port_id(), &stats1);
+
+ stats->ipackets += stats1.ipackets - prev_stats->ipackets;
+ stats->ibytes += stats1.ibytes - prev_stats->ibytes
+ - ((stats1.ipackets << 2) - (prev_stats->ipackets << 2));
+ stats->opackets += stats1.opackets - prev_stats->opackets;
+ stats->obytes += stats1.obytes - prev_stats->obytes;
+ stats->f_ipackets += 0;
+ stats->f_ibytes += 0;
+ stats->ierrors += stats1.imissed + stats1.ierrors + stats1.rx_nombuf
+ - prev_stats->imissed - prev_stats->ierrors - prev_stats->rx_nombuf;
+ stats->oerrors += stats1.oerrors - prev_stats->oerrors;
+ stats->imcasts += 0;
+ stats->rx_nombuf += stats1.rx_nombuf - prev_stats->rx_nombuf;
+
+ prev_stats->ipackets = stats1.ipackets;
+ prev_stats->ibytes = stats1.ibytes;
+ prev_stats->opackets = stats1.opackets;
+ prev_stats->obytes = stats1.obytes;
+ prev_stats->imissed = stats1.imissed;
+ prev_stats->oerrors = stats1.oerrors;
+ prev_stats->ierrors = stats1.ierrors;
+ prev_stats->rx_nombuf = stats1.rx_nombuf;
+}
+
+int CTRexExtendedDriverBaseVIC::verify_fw_ver(int port_id) {
+
+ struct rte_eth_fdir_info fdir_info;
+
+ if ( rte_eth_dev_filter_ctrl(port_id,RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_INFO,(void *)&fdir_info) == 0 ){
+ if ( fdir_info.flow_types_mask[0] & (1<< RTE_ETH_FLOW_NONFRAG_IPV4_OTHER) ) {
+ /* support new features */
+ if (CGlobalInfo::m_options.preview.getVMode() >= 1) {
+ printf("VIC port %d: FW support advanced filtering \n", port_id);
+ }
+ return (0);
+ }
+ }
+
+ printf("Error: VIC firmware should upgrade to support advanced filtering \n");
+ printf(" Please refer to %s for upgrade instructions\n",
+ "https://trex-tgn.cisco.com/trex/doc/trex_manual.html");
+ exit(1);
+}
+
+int CTRexExtendedDriverBaseVIC::configure_rx_filter_rules(CPhyEthIF * _if) {
+
+ if (get_is_stateless()) {
+ /* both stateless and stateful work in the same way, might changed in the future TOS */
+ return configure_rx_filter_rules_statefull(_if);
+ } else {
+ return configure_rx_filter_rules_statefull(_if);
+ }
+}
+
+void CTRexExtendedDriverBaseVIC::reset_rx_stats(CPhyEthIF * _if, uint32_t *stats, int min, int len) {
+}
+
+int CTRexExtendedDriverBaseVIC::get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts
+ ,uint32_t *bytes, uint32_t *prev_bytes, int min, int max) {
+ printf(" NOT supported yet \n");
+ return 0;
+}
+
+// if fd != NULL, dump fdir stats of _if
+// return num of filters
+int CTRexExtendedDriverBaseVIC::dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd)
+{
+ //printf(" NOT supported yet \n");
+ return (0);
+}
+
+CFlowStatParser *CTRexExtendedDriverBaseVIC::get_flow_stat_parser() {
+ CFlowStatParser *parser = new CFlowStatParser();
+ assert (parser);
+ return parser;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////
void CTRexExtendedDriverBase1GVm::update_configuration(port_cfg_t * cfg){
@@ -6495,19 +7253,10 @@ TrexDpdkPlatformApi::get_interface_info(uint8_t interface_id, intf_info_st &info
/* mac INFO */
/* hardware */
- g_trex.m_ports[interface_id].get_port_attr()->macaddr_get(&rte_mac_addr);
+ g_trex.m_ports[interface_id].get_port_attr()->get_hw_src_mac(&rte_mac_addr);
assert(ETHER_ADDR_LEN == 6);
- /* software */
- uint8_t sw_macaddr[12];
- memcpy(sw_macaddr, CGlobalInfo::m_options.get_dst_src_mac_addr(interface_id), 12);
-
- for (int i = 0; i < 6; i++) {
- info.mac_info.hw_macaddr[i] = rte_mac_addr.addr_bytes[i];
- info.mac_info.dst_macaddr[i] = sw_macaddr[i];
- info.mac_info.src_macaddr[i] = sw_macaddr[6 + i];
-
- }
+ memcpy(info.hw_macaddr, rte_mac_addr.addr_bytes, 6);
info.numa_node = g_trex.m_ports[interface_id].m_dev_info.pci_dev->numa_node;
struct rte_pci_addr *loc = &g_trex.m_ports[interface_id].m_dev_info.pci_dev->addr;
@@ -6613,6 +7362,30 @@ TRexPortAttr *TrexDpdkPlatformApi::getPortAttrObj(uint8_t port_id) const {
return g_trex.m_ports[port_id].get_port_attr();
}
+
+int DpdkTRexPortAttr::set_rx_filter_mode(rx_filter_mode_e rx_filter_mode) {
+
+ if (rx_filter_mode == m_rx_filter_mode) {
+ return (0);
+ }
+
+ CPhyEthIF *_if = &g_trex.m_ports[m_port_id];
+ bool recv_all = (rx_filter_mode == RX_FILTER_MODE_ALL);
+ int rc = CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(_if, recv_all);
+ if (rc != 0) {
+ return (rc);
+ }
+
+ m_rx_filter_mode = rx_filter_mode;
+
+ return (0);
+}
+
+bool DpdkTRexPortAttr::is_loopback() const {
+ uint8_t port_id;
+ return g_trex.lookup_port_by_mac(m_dest.get_dest_mac(), port_id);
+}
+
/**
* marks the control plane for a total server shutdown
*
@@ -6621,3 +7394,4 @@ TRexPortAttr *TrexDpdkPlatformApi::getPortAttrObj(uint8_t port_id) const {
void TrexDpdkPlatformApi::mark_for_shutdown() const {
g_trex.mark_for_shutdown(CGlobalTRex::SHUTDOWN_RPC_REQ);
}
+
diff --git a/src/main_dpdk.h b/src/main_dpdk.h
index 6402d106..25b19471 100644
--- a/src/main_dpdk.h
+++ b/src/main_dpdk.h
@@ -109,6 +109,9 @@ class CPhyEthIF {
void dump_stats(FILE *fd);
void set_ignore_stats_base(CPreTestStats &pre_stats);
void update_counters();
+ void configure_rss_redirect_table(uint16_t numer_of_queues,
+ uint16_t skip_queue);
+
void stats_clear();
uint8_t get_port_id(){
return (m_port_id);
diff --git a/src/pal/linux_dpdk/x86_64-default-linuxapp-gcc/include/rte_config.h b/src/pal/linux_dpdk/x86_64-default-linuxapp-gcc/include/rte_config.h
index fdb5b994..25f8b4fe 100755
--- a/src/pal/linux_dpdk/x86_64-default-linuxapp-gcc/include/rte_config.h
+++ b/src/pal/linux_dpdk/x86_64-default-linuxapp-gcc/include/rte_config.h
@@ -70,3 +70,11 @@
#undef RTE_TEST_PMD_RECORD_BURST_STATS
#undef RTE_LIBRTE_GCOV
#undef RTE_INSECURE_FUNCTION_WARNING
+
+
+//#undef RTE_LIBRTE_MLX5_PMD
+//#define RTE_LIBRTE_MLX5_PMD 1
+//#undef RTE_LIBRTE_MLX5_TX_MP_CACHE
+//#define RTE_LIBRTE_MLX5_TX_MP_CACHE 8
+//#define MLX5_FDIR_SUPPORT 1
+
diff --git a/src/pkt_gen.cpp b/src/pkt_gen.cpp
index eb9a26f9..656b1b06 100644
--- a/src/pkt_gen.cpp
+++ b/src/pkt_gen.cpp
@@ -30,7 +30,7 @@
#include <common/Network/Packet/Arp.h>
#include "rx_check_header.h"
#include "pkt_gen.h"
-
+#include "bp_sim.h"
// For use in tests
char *CTestPktGen::create_test_pkt(uint16_t l3_type, uint16_t l4_proto, uint8_t ttl, uint32_t ip_id, uint16_t flags
, uint16_t max_payload, int &pkt_size) {
@@ -52,7 +52,7 @@ char *CTestPktGen::create_test_pkt(uint16_t l3_type, uint16_t l4_proto, uint8_t
}
uint8_t ip_header[] = {
- 0x45,0x02,0x00,0x30,
+ 0x45,0x03,0x00,0x30,
0x00,0x00,0x40,0x00,
0xff,0x01,0xbd,0x04,
0x10,0x0,0x0,0x1, //SIP
@@ -60,7 +60,7 @@ char *CTestPktGen::create_test_pkt(uint16_t l3_type, uint16_t l4_proto, uint8_t
// 0x82, 0x0b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // IP option. change 45 to 48 (header len) if using it.
};
uint8_t ipv6_header[] = {
- 0x60,0x00,0xff,0x30, // traffic class + flow label
+ 0x60,0x10,0xff,0x30, // traffic class + flow label
0x00,0x00,0x40,0x00, // payload len + next header + hop limit
0x10,0x0,0x0,0x1,0x10,0x0,0x0,0x1,0x10,0x0,0x0,0x1,0x10,0x0,0x0,0x1, //SIP
0x30,0x0,0x0,0x1,0x10,0x0,0x0,0x1,0x30,0x0,0x0,0x1,0x10,0x0,0x0,0x1, //DIP
@@ -222,10 +222,22 @@ char *CTestPktGen::create_test_pkt(uint16_t l3_type, uint16_t l4_proto, uint8_t
switch(l3_type) {
case EthernetHeader::Protocol::IP:
ip->setTimeToLive(ttl);
+ if (flags & DPF_TOS_1) {
+ ip->setTOS(TOS_TTL_RESERVE_DUPLICATE);
+ }else{
+ ip->setTOS(0x2);
+ }
+
ip->updateCheckSum();
break;
case EthernetHeader::Protocol::IPv6:
ipv6->setHopLimit(ttl);
+ if (flags & DPF_TOS_1) {
+ ipv6->setTrafficClass(TOS_TTL_RESERVE_DUPLICATE);
+ }else{
+ ipv6->setTrafficClass(0x2);
+ }
+
break;
}
diff --git a/src/pkt_gen.h b/src/pkt_gen.h
index 309e02b9..8dcba624 100644
--- a/src/pkt_gen.h
+++ b/src/pkt_gen.h
@@ -37,7 +37,8 @@ enum {
enum {
DPF_VLAN = 0x1,
DPF_QINQ = 0X2,
- DPF_RXCHECK = 0x4
+ DPF_RXCHECK = 0x4,
+ DPF_TOS_1 = 0x8,
};
class CTestPktGen {
diff --git a/src/pre_test.cpp b/src/pre_test.cpp
index 130d076d..7127645d 100644
--- a/src/pre_test.cpp
+++ b/src/pre_test.cpp
@@ -29,80 +29,371 @@
#include "pkt_gen.h"
#include "pre_test.h"
-void CPretestPortInfo::set_params(CPerPortIPCfg port_cfg, const uint8_t *src_mac, bool resolve_needed) {
- m_ip = port_cfg.get_ip();
- m_def_gw = port_cfg.get_def_gw();
- m_vlan = port_cfg.get_vlan();
- memcpy(&m_src_mac, src_mac, sizeof(m_src_mac));
- if (resolve_needed) {
- m_state = CPretestPortInfo::RESOLVE_NEEDED;
- } else {
- m_state = CPretestPortInfo::RESOLVE_NOT_NEEDED;
+CPretestOnePortInfo::CPretestOnePortInfo() {
+ m_state = RESOLVE_NOT_NEEDED;
+ m_is_loopback = false;
+ m_stats.clear();
+}
+
+CPretestOnePortInfo::~CPretestOnePortInfo() {
+ for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
+ delete *it;
+ }
+ for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
+ delete *it;
}
}
-void CPretestPortInfo::set_dst_mac(const uint8_t *dst_mac) {
- memcpy(&m_dst_mac, dst_mac, sizeof(m_dst_mac));
- m_state = CPretestPortInfo::RESOLVE_DONE;
+void CPretestOnePortInfo::add_src(uint32_t ip, uint16_t vlan, MacAddress mac) {
+ COneIPv4Info *one_ip = new COneIPv4Info(ip, vlan, mac);
+ assert(one_ip);
+ m_src_info.push_back(one_ip);
}
-void CPretestPortInfo::dump(FILE *fd) {
- if (m_state == INIT_NEEDED) {
- return;
- }
+void CPretestOnePortInfo::add_dst(uint32_t ip, uint16_t vlan) {
+ MacAddress default_mac;
+ COneIPv4Info *one_ip = new COneIPv4Info(ip, vlan, default_mac);
+ assert(one_ip);
+ m_dst_info.push_back(one_ip);
+ m_state = RESOLVE_NEEDED;
+}
- uint32_t ip = htonl(m_ip);
+void CPretestOnePortInfo::add_src(uint16_t ip[8], uint16_t vlan, MacAddress mac) {
+ COneIPv6Info *one_ip = new COneIPv6Info(ip, vlan, mac);
+ assert(one_ip);
+ m_src_info.push_back(one_ip);
+}
- fprintf(fd, " ip:%d.%d.%d.%d", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
- ip = htonl(m_def_gw);
- fprintf(fd, " default gw:%d.%d.%d.%d\n", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
+void CPretestOnePortInfo::add_dst(uint16_t ip[8], uint16_t vlan) {
+ MacAddress default_mac;
+ COneIPv6Info *one_ip = new COneIPv6Info(ip, vlan, default_mac);
+ assert(one_ip);
+ m_dst_info.push_back(one_ip);
+ m_state = RESOLVE_NEEDED;
+}
- printf(" src MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", m_src_mac[0], m_src_mac[1], m_src_mac[2], m_src_mac[3]
- , m_src_mac[4], m_src_mac[5]);
- printf( " dst MAC: ");
- if (m_state == RESOLVE_DONE) {
- printf("%02x:%02x:%02x:%02x:%02x:%02x\n", m_dst_mac[0], m_dst_mac[1], m_dst_mac[2], m_dst_mac[3]
- , m_dst_mac[4], m_dst_mac[5]);
- } else {
- printf("Not resolved\n");
+void CPretestOnePortInfo::dump(FILE *fd, char *offset) {
+ std::string new_offset = std::string(offset) + " ";
+
+ if (m_is_loopback) {
+ fprintf(fd, "%sPort connected in loopback\n", offset);
+ }
+ fprintf(fd, "%sSources:\n", offset);
+ for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
+ (*it)->dump(fd, new_offset.c_str());
+ }
+ fprintf(fd, "%sDestinations:\n", offset);
+ for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
+ (*it)->dump(fd, new_offset.c_str());
}
}
/*
- put in mac relevant dest MAC for port/ip pair.
- return false if no relevant info exists, true otherwise.
+ * Get appropriate source for given vlan and ip version.
*/
-bool CPretest::get_mac(uint16_t port_id, uint32_t ip, uint8_t *mac) {
- assert(port_id < TREX_MAX_PORTS);
+COneIPInfo *CPretestOnePortInfo::get_src(uint16_t vlan, uint8_t ip_ver) {
+ for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
+ if ((ip_ver == (*it)->ip_ver()) && (vlan == (*it)->get_vlan()))
+ return (*it);
+ }
+
+ return NULL;
+}
+
+COneIPv4Info *CPretestOnePortInfo::find_ip(uint32_t ip, uint16_t vlan) {
+ for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
+ if (((*it)->ip_ver() == COneIPInfo::IP4_VER) && ((*it)->get_vlan() == vlan) && (((COneIPv4Info *)(*it))->get_ip() == ip))
+ return (COneIPv4Info *) *it;
+ }
+
+ return NULL;
+}
+
+COneIPv4Info *CPretestOnePortInfo::find_next_hop(uint32_t ip, uint16_t vlan) {
+
+ for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
+ if (((*it)->ip_ver() == COneIPInfo::IP4_VER) && ((*it)->get_vlan() == vlan) && (((COneIPv4Info *)(*it))->get_ip() == ip))
+ return (COneIPv4Info *) *it;
+ }
+
+ return NULL;
+}
+
+COneIPv6Info *CPretestOnePortInfo::find_ipv6(uint16_t ip[8], uint16_t vlan) {
+ for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
+ if (((*it)->ip_ver() == COneIPInfo::IP6_VER) && ((*it)->get_vlan() == vlan)
+ && (! memcmp((uint8_t *) ((COneIPv6Info *) (*it))->get_ipv6(), (uint8_t *)ip, 2*8 /* ???*/ ) ) )
+ return (COneIPv6Info *) *it;
+ }
+
+ return NULL;
+}
+
+bool CPretestOnePortInfo::get_mac(COneIPInfo *ip, uint8_t *mac) {
+ MacAddress defaultmac;
+
+ for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
+ if (ip->ip_ver() != (*it)->ip_ver())
+ continue;
- if (m_port_info[port_id].m_state != CPretestPortInfo::RESOLVE_DONE) {
+ switch(ip->ip_ver()) {
+ case 4:
+ if (*((COneIPv4Info *) (*it)) != *((COneIPv4Info *) ip))
+ continue;
+ break;
+ case 6:
+ if (*((COneIPv6Info *) (*it)) != *((COneIPv6Info *) ip))
+ continue;
+ break;
+ default:
+ assert(0);
+ }
+
+ (*it)->get_mac(mac);
+ if (! memcmp(mac, defaultmac.GetConstBuffer(), ETHER_ADDR_LEN)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CPretestOnePortInfo::get_mac(uint32_t ip, uint16_t vlan, uint8_t *mac) {
+ COneIPv4Info one_ip(ip, vlan);
+
+ return get_mac(&one_ip, mac);
+}
+
+bool CPretestOnePortInfo::get_mac(uint16_t ip[8], uint16_t vlan, uint8_t *mac) {
+ COneIPv6Info one_ip(ip, vlan);
+
+ return get_mac(&one_ip, mac);
+}
+
+// return true if there are still any addresses to resolve on this port
+bool CPretestOnePortInfo::resolve_needed() {
+ if (m_state == RESOLVE_NOT_NEEDED)
return false;
+
+ for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
+ if ((*it)->resolve_needed())
+ return true;
}
- memcpy(mac, &m_port_info[port_id].m_dst_mac, sizeof(m_port_info[port_id].m_dst_mac));
+ m_state = RESOLVE_NOT_NEEDED;
+ return false;
+}
+
+void CPretestOnePortInfo::send_arp_req_all() {
+ for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
+ rte_mbuf_t *m[1];
+ int num_sent;
+ int verbose = CGlobalInfo::m_options.preview.getVMode();
- return true;
+ m[0] = CGlobalInfo::pktmbuf_alloc_small_by_port(m_port_id);
+ if ( unlikely(m[0] == 0) ) {
+ fprintf(stderr, "ERROR: Could not allocate mbuf for sending ARP to port:%d\n", m_port_id);
+ exit(1);
+ }
+
+ uint8_t *p = (uint8_t *)rte_pktmbuf_append(m[0], (*it)->get_arp_req_len());
+ // We need source on the same VLAN of the dest in order to send
+ COneIPInfo *sip = get_src((*it)->get_vlan(), (*it)->ip_ver());
+ if (sip == NULL) {
+ fprintf(stderr, "Failed finding matching source for - ");
+ (*it)->dump(stderr);
+ exit(1);
+ }
+ (*it)->fill_arp_req_buf(p, m_port_id, sip);
+
+ if (verbose >= 3) {
+ fprintf(stdout, "TX ARP request on port %d - " , m_port_id);
+ (*it)->dump(stdout, "");
+ }
+
+ num_sent = rte_eth_tx_burst(m_port_id, 0, m, 1);
+ if (num_sent < 1) {
+ fprintf(stderr, "Failed sending ARP to port:%d\n", m_port_id);
+ exit(1);
+ } else {
+ m_stats.m_tx_arp++;
+ }
+ }
+}
+
+void CPretestOnePortInfo::send_grat_arp_all() {
+ for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
+ rte_mbuf_t *m[1];
+ int num_sent;
+ int verbose = CGlobalInfo::m_options.preview.getVMode();
+
+ m[0] = CGlobalInfo::pktmbuf_alloc_small_by_port(m_port_id);
+ if ( unlikely(m[0] == 0) ) {
+ fprintf(stderr, "ERROR: Could not allocate mbuf for sending grat ARP on port:%d\n", m_port_id);
+ exit(1);
+ }
+
+ uint8_t *p = (uint8_t *)rte_pktmbuf_append(m[0], (*it)->get_grat_arp_len());
+ (*it)->fill_grat_arp_buf(p);
+
+
+ if (verbose >= 3) {
+ fprintf(stdout, "TX grat ARP on port %d - " , m_port_id);
+ (*it)->dump(stdout, "");
+ }
+
+ num_sent = rte_eth_tx_burst(m_port_id, 0, m, 1);
+ if (num_sent < 1) {
+ fprintf(stderr, "Failed sending grat ARP on port:%d\n", m_port_id);
+ exit(1);
+ } else {
+ m_stats.m_tx_arp++;
+ }
+ }
+}
+
+// IPv4 functions
+void CPretest::add_ip(uint16_t port, uint32_t ip, uint16_t vlan, MacAddress src_mac) {
+ assert(port < m_max_ports);
+ m_port_info[port].add_src(ip, vlan, src_mac);
+}
+
+void CPretest::add_ip(uint16_t port, uint32_t ip, MacAddress src_mac) {
+ assert(port < m_max_ports);
+ add_ip(port, ip, 0, src_mac);
+}
+
+void CPretest::add_next_hop(uint16_t port, uint32_t ip, uint16_t vlan) {
+ assert(port < m_max_ports);
+ m_port_info[port].add_dst(ip, vlan);
+}
+
+void CPretest::add_next_hop(uint16_t port, uint32_t ip) {
+ assert(port < m_max_ports);
+ add_next_hop(port, ip, 0);
+}
+
+// IPv6 functions
+void CPretest::add_ip(uint16_t port, uint16_t ip[8], uint16_t vlan, MacAddress src_mac) {
+ assert(port < m_max_ports);
+ m_port_info[port].add_src(ip, vlan, src_mac);
+}
+
+void CPretest::add_ip(uint16_t port, uint16_t ip[8], MacAddress src_mac) {
+ assert(port < m_max_ports);
+ add_ip(port, ip, 0, src_mac);
+}
+
+void CPretest::add_next_hop(uint16_t port, uint16_t ip[8], uint16_t vlan) {
+ assert(port < m_max_ports);
+ m_port_info[port].add_dst(ip, vlan);
+}
+
+void CPretest::add_next_hop(uint16_t port, uint16_t ip[8]) {
+ assert(port < m_max_ports);
+ add_next_hop(port, ip, 0);
+}
+
+// put in mac, the relevant mac address for the tupple port_id, ip, vlan
+bool CPretest::get_mac(uint16_t port_id, uint32_t ip, uint16_t vlan, uint8_t *mac) {
+ assert(port_id < m_max_ports);
+
+ return m_port_info[port_id].get_mac(ip, vlan, mac);
+}
+
+// IPv6 version of above
+bool CPretest::get_mac(uint16_t port_id, uint16_t ip[8], uint16_t vlan, uint8_t *mac) {
+ assert(port_id < m_max_ports);
+
+ return m_port_info[port_id].get_mac(ip, vlan, mac);
}
CPreTestStats CPretest::get_stats(uint16_t port_id) {
- assert(port_id < TREX_MAX_PORTS);
+ assert(port_id < m_max_ports);
- return m_port_info[port_id].m_stats;
+ return m_port_info[port_id].get_stats();
}
bool CPretest::is_loopback(uint16_t port) {
- assert(port < TREX_MAX_PORTS);
+ assert(port < m_max_ports);
+
+ return m_port_info[port].is_loopback();
+}
+
+bool CPretest::resolve_all() {
+ uint16_t port;
+
+ // send ARP request on all ports
+ for (port = 0; port < m_max_ports; port++) {
+ m_port_info[port].send_arp_req_all();
+ }
+
+ int max_tries = 1000;
+ int i;
+ for (i = 0; i < max_tries; i++) {
+ bool all_resolved = true;
+ for (port = 0; port < m_max_ports; port++) {
+ if (m_port_info[port].resolve_needed()) {
+ // We need to stop reading packets only if all ports are resolved.
+ // If we are on loopback, We might get requests on port even after it is in RESOLVE_DONE state
+ all_resolved = false;
+ }
+ handle_rx(port, MAIN_DPDK_DATA_Q);
+ if (! CGlobalInfo::m_options.preview.get_vm_one_queue_enable())
+ handle_rx(port, MAIN_DPDK_RX_Q);
+ }
+ if (all_resolved) {
+ break;
+ } else {
+ delay(1);
+ }
+ }
- return m_port_info[port].m_is_loopback;
+ if (i == max_tries) {
+ return false;
+ } else {
+ return true;
+ }
+
+ return true;
}
-void CPretest::set_port_params(uint16_t port_id, const CPerPortIPCfg &port_cfg, const uint8_t *src_mac, bool resolve_needed) {
- if (port_id >= m_max_ports)
- return;
+void CPretest::send_arp_req_all() {
+ for (uint16_t port = 0; port < m_max_ports; port++) {
+ m_port_info[port].send_arp_req_all();
+ }
+}
- m_port_info[port_id].set_params(port_cfg, src_mac, resolve_needed);
+void CPretest::send_grat_arp_all() {
+ for (uint16_t port = 0; port < m_max_ports; port++) {
+ m_port_info[port].send_grat_arp_all();
+ }
}
+bool CPretest::is_arp(const uint8_t *p, uint16_t pkt_size, ArpHdr *&arp, uint16_t &vlan_tag) {
+ EthernetHeader *m_ether = (EthernetHeader *)p;
+ vlan_tag = 0;
+
+ if ((pkt_size < sizeof(EthernetHeader)) ||
+ ((m_ether->getNextProtocol() != EthernetHeader::Protocol::ARP)
+ && (m_ether->getNextProtocol() != EthernetHeader::Protocol::VLAN)))
+ return false;
+
+ if (m_ether->getNextProtocol() == EthernetHeader::Protocol::ARP) {
+ arp = (ArpHdr *)(p + 14);
+ } else {
+ if (m_ether->getVlanProtocol() != EthernetHeader::Protocol::ARP) {
+ return false;
+ } else {
+ vlan_tag = m_ether->getVlanTag();
+ arp = (ArpHdr *)(p + 18);
+ }
+ }
+
+ return true;
+}
int CPretest::handle_rx(int port_id, int queue_id) {
rte_mbuf_t * rx_pkts[32];
@@ -121,72 +412,89 @@ int CPretest::handle_rx(int port_id, int queue_id) {
int pkt_size = rte_pktmbuf_pkt_len(m);
uint8_t *p = rte_pktmbuf_mtod(m, uint8_t *);
ArpHdr *arp;
- CPretestPortInfo *port = &m_port_info[port_id];
- if (is_arp(p, pkt_size, arp)) {
- m_port_info[port_id].m_stats.m_rx_arp++;
+ uint16_t vlan_tag;
+ CPretestOnePortInfo *port = &m_port_info[port_id];
+ if (is_arp(p, pkt_size, arp, vlan_tag)) {
+ port->m_stats.m_rx_arp++;
if (arp->m_arp_op == htons(ArpHdr::ARP_HDR_OP_REQUEST)) {
if (verbose >= 3) {
- fprintf(stdout, "RX ARP request on port %d queue %d sip:0x%08x tip:0x%08x\n"
- , port_id, queue_id, ntohl(arp->m_arp_sip), ntohl(arp->m_arp_tip));
+ bool is_grat = false;
+ if (arp->m_arp_sip == arp->m_arp_tip) {
+ is_grat = true;
+ }
+ fprintf(stdout, "RX %s on port %d queue %d sip:%s tip:%s vlan:%d\n"
+ , is_grat ? "grat ARP" : "ARP request"
+ , port_id, queue_id
+ , ip_to_str(ntohl(arp->m_arp_sip)).c_str()
+ , ip_to_str(ntohl(arp->m_arp_tip)).c_str()
+ , vlan_tag);
}
// is this request for our IP?
- if (ntohl(arp->m_arp_tip) == port->m_ip) {
+ COneIPv4Info *src_addr;
+ COneIPv4Info *rcv_addr;
+ if ((src_addr = port->find_ip(ntohl(arp->m_arp_tip), vlan_tag))) {
// If our request(i.e. we are connected in loopback)
// , do a shortcut, and write info directly to asking port
uint8_t magic[5] = {0x1, 0x3, 0x5, 0x7, 0x9};
if (! memcmp((uint8_t *)&arp->m_arp_tha.data, magic, 5)) {
uint8_t sent_port_id = arp->m_arp_tha.data[5];
if ((sent_port_id < m_max_ports) &&
- (m_port_info[sent_port_id].m_def_gw == port->m_ip)) {
- memcpy(m_port_info[sent_port_id].m_dst_mac, port->m_src_mac, ETHER_ADDR_LEN);
- m_port_info[sent_port_id].m_state = CPretestPortInfo::RESOLVE_DONE;
+ (rcv_addr = m_port_info[sent_port_id].find_next_hop(ntohl(arp->m_arp_tip), vlan_tag))) {
+ uint8_t mac[ETHER_ADDR_LEN];
+ src_addr->get_mac(mac);
+ rcv_addr->set_mac(mac);
+ port->m_is_loopback = true;
m_port_info[sent_port_id].m_is_loopback = true;
}
} else {
// Not our request. Answer.
+ uint8_t src_mac[ETHER_ADDR_LEN];
free_pkt = false; // We use the same mbuf to send response. Don't free it twice.
arp->m_arp_op = htons(ArpHdr::ARP_HDR_OP_REPLY);
uint32_t tmp_ip = arp->m_arp_sip;
arp->m_arp_sip = arp->m_arp_tip;
arp->m_arp_tip = tmp_ip;
memcpy((uint8_t *)&arp->m_arp_tha, (uint8_t *)&arp->m_arp_sha, ETHER_ADDR_LEN);
- memcpy((uint8_t *)&arp->m_arp_sha, port->m_src_mac, ETHER_ADDR_LEN);
+ src_addr->get_mac(src_mac);
+ memcpy((uint8_t *)&arp->m_arp_sha, src_mac, ETHER_ADDR_LEN);
EthernetHeader *m_ether = (EthernetHeader *)p;
memcpy((uint8_t *)&m_ether->myDestination, (uint8_t *)&m_ether->mySource, ETHER_ADDR_LEN);
- memcpy((uint8_t *)&m_ether->mySource, (uint8_t *)port->m_src_mac, ETHER_ADDR_LEN);
+ memcpy((uint8_t *)&m_ether->mySource, src_mac, ETHER_ADDR_LEN);
int num_sent = rte_eth_tx_burst(port_id, 0, &m, 1);
if (num_sent < 1) {
fprintf(stderr, "Failed sending ARP reply to port:%d\n", port_id);
rte_pktmbuf_free(m);
} else {
- fprintf(stdout, "TX ARP reply on port:%d sip:0x%08x, tip:0x%08x\n"
- , port_id ,htonl(arp->m_arp_sip), htonl(arp->m_arp_tip));
+ if (verbose >= 3) {
+ fprintf(stdout, "TX ARP reply on port:%d sip:%s, tip:%s\n"
+ , port_id
+ , ip_to_str(ntohl(arp->m_arp_sip)).c_str()
+ , ip_to_str(ntohl(arp->m_arp_tip)).c_str());
+
+ }
m_port_info[port_id].m_stats.m_tx_arp++;
}
}
} else {
- // ARP request not to our IP.
- if ((ntohl(arp->m_arp_tip) == port->m_def_gw) && (ntohl(arp->m_arp_sip) == port->m_def_gw)) {
- // sip and tip equals def_gw, meaning we got gratitues ARP.
- port->set_dst_mac((uint8_t *)&arp->m_arp_sha);
+ // ARP request not to our IP. Check if this is gratitues ARP for something we need.
+ if ((arp->m_arp_tip == arp->m_arp_sip)
+ && (rcv_addr = port->find_next_hop(ntohl(arp->m_arp_tip), vlan_tag))) {
+ rcv_addr->set_mac((uint8_t *)&arp->m_arp_sha);
}
}
} else {
if (arp->m_arp_op == htons(ArpHdr::ARP_HDR_OP_REPLY)) {
if (verbose >= 3) {
- bool is_grat = false;
- if (arp->m_arp_sip == arp->m_arp_tip) {
- is_grat = true;
- }
- fprintf(stdout, "RX %s on port %d queue %d sip:0x%08x tip:0x%08x\n"
- , is_grat ? "grat ARP" : "ARP reply"
+ fprintf(stdout, "RX ARP reply on port %d queue %d sip:%s tip:%s\n"
, port_id, queue_id
- , ntohl(arp->m_arp_sip)
- , ntohl(arp->m_arp_tip));
+ , ip_to_str(ntohl(arp->m_arp_sip)).c_str()
+ , ip_to_str(ntohl(arp->m_arp_tip)).c_str());
}
+
// If this is response to our request, update our tables
- if (port->m_def_gw == ntohl(arp->m_arp_sip)) {
- port->set_dst_mac((uint8_t *)&arp->m_arp_sha);
+ COneIPv4Info *addr;
+ if ((addr = port->find_next_hop(ntohl(arp->m_arp_sip), vlan_tag))) {
+ addr->set_mac((uint8_t *)&arp->m_arp_sha);
}
}
}
@@ -199,160 +507,87 @@ int CPretest::handle_rx(int port_id, int queue_id) {
return 0;
}
-/*
- Try to resolve def_gw address on all ports marked as needed.
- Return false if failed to resolve on one of the ports
- */
-bool CPretest::resolve_all() {
- uint16_t port;
-
- // send ARP request on all ports
- for (port = 0; port < m_max_ports; port++) {
- if (m_port_info[port].m_state == CPretestPortInfo::RESOLVE_NEEDED) {
- send_arp_req(port, false);
- }
- }
-
- int max_tries = 1000;
- int i;
- for (i = 0; i < max_tries; i++) {
- bool all_resolved = true;
- for (port = 0; port < m_max_ports; port++) {
- if (m_port_info[port].m_state == CPretestPortInfo::RESOLVE_NEEDED) {
- // We need to stop reading packets only if all ports are resolved.
- // If we are on loopback, We might get requests on port even after it is in RESOLVE_DONE state
- all_resolved = false;
+void CPretest::get_results(CManyIPInfo &resolved_ips) {
+ for (int port = 0; port < m_max_ports; port++) {
+ for (std::vector<COneIPInfo *>::iterator it = m_port_info[port].m_dst_info.begin()
+ ; it != m_port_info[port].m_dst_info.end(); ++it) {
+ uint8_t ip_type = (*it)->ip_ver();
+ (*it)->set_port(port);
+ switch(ip_type) {
+ case COneIPInfo::IP4_VER:
+ resolved_ips.insert(*(COneIPv4Info *)(*it));
+ break;
+#if 0
+ //??? fix for ipv6
+ case COneIPInfo::IP6_VER:
+ ipv6_tmp = (uint8_t *)((COneIPv6Info *)(*it))->get_ipv6();
+ memcpy((uint8_t *)ipv6, (uint8_t *)ipv6_tmp, 16);
+ v6_list.insert(std::pair<std::pair<uint16_t[8], uint16_t>, COneIPv6Info>
+ (std::pair<uint16_t[8], uint16_t>(ipv6, vlan), *(COneIPv6Info *)(*it)));
+ break;
+#endif
+ default:
+ break;
}
- handle_rx(port, MAIN_DPDK_DATA_Q);
- if (! CGlobalInfo::m_options.preview.get_vm_one_queue_enable())
- handle_rx(port, MAIN_DPDK_RX_Q);
- }
- if (all_resolved) {
- break;
- } else {
- delay(1);
}
}
-
- if (i == max_tries) {
- return false;
- } else {
- return true;
- }
}
void CPretest::dump(FILE *fd) {
+ fprintf(fd, "Pre test info start ===================\n");
for (int port = 0; port < m_max_ports; port++) {
- if (m_port_info[port].m_state != CPretestPortInfo::INIT_NEEDED) {
- fprintf(fd, "port %d:\n", port);
- m_port_info[port].dump(fd);
- }
- }
-}
-
-// Send ARP request for our default gateway on port
-// If is_grat is true - send gratuitous ARP.
-void CPretest::send_arp_req(uint16_t port_id, bool is_grat) {
- rte_mbuf_t *m[1];
- int num_sent;
- int verbose = CGlobalInfo::m_options.preview.getVMode();
-
- m[0] = CGlobalInfo::pktmbuf_alloc_small(0);
- if ( unlikely(m[0] == 0) ) {
- fprintf(stderr, "ERROR: Could not allocate mbuf for sending ARP to port:%d\n", port_id);
- exit(1);
- }
-
- uint32_t tip;
- uint8_t *p = (uint8_t *)rte_pktmbuf_append(m[0], 60); // ARP packet is shorter than 60
- uint32_t sip = m_port_info[port_id].m_ip;
- uint8_t *src_mac = m_port_info[port_id].m_src_mac;
- uint16_t vlan = m_port_info[port_id].m_vlan;
- if (is_grat) {
- tip = sip;
- } else {
- tip = m_port_info[port_id].m_def_gw;
- }
-
- if (verbose >= 3) {
- fprintf(stdout, "TX %s port:%d sip:0x%08x, tip:0x%08x\n"
- , is_grat ? "grat ARP": "ARP request", port_id ,sip, tip);
- }
-
- CTestPktGen::create_arp_req(p, sip, tip, src_mac, vlan, port_id);
- num_sent = rte_eth_tx_burst(port_id, 0, m, 1);
- if (num_sent < 1) {
- fprintf(stderr, "Failed sending ARP to port:%d\n", port_id);
- exit(1);
- } else {
- m_port_info[port_id].m_stats.m_tx_arp++;
- }
-}
-
-/*
- Send gratuitous ARP on all ports
- */
-void CPretest::send_grat_arp_all() {
- for (uint16_t port = 0; port < m_max_ports; port++) {
- if (m_port_info[port].m_state == CPretestPortInfo::RESOLVE_NEEDED) {
- send_arp_req(port, true);
- }
- }
-}
-
-bool CPretest::is_arp(const uint8_t *p, uint16_t pkt_size, ArpHdr *&arp) {
- EthernetHeader *m_ether = (EthernetHeader *)p;
-
- if ((pkt_size < 60) ||
- ((m_ether->getNextProtocol() != EthernetHeader::Protocol::ARP)
- && (m_ether->getNextProtocol() != EthernetHeader::Protocol::VLAN)))
- return false;
-
- if (m_ether->getNextProtocol() == EthernetHeader::Protocol::ARP) {
- arp = (ArpHdr *)(p + 14);
- } else {
- if (m_ether->getVlanProtocol() != EthernetHeader::Protocol::ARP) {
- return false;
- } else {
- arp = (ArpHdr *)(p + 18);
- }
+ fprintf(fd, "Port %d:\n", port);
+ m_port_info[port].dump(fd, (char *)" ");
}
-
- return true;
+ fprintf(fd, "Pre test info end ===================\n");
}
-// Should be run on setup with two interfaces connected by loopback.
-// Before running, should put ports on receive all mode.
void CPretest::test() {
uint8_t found_mac[ETHER_ADDR_LEN];
uint8_t mac0[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd0};
uint8_t mac1[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd1};
- uint32_t ip0 = 0x0f000003;
- uint32_t ip1 = 0x0f000001;
-
- CPerPortIPCfg port_cfg0;
- CPerPortIPCfg port_cfg1;
- port_cfg0.set_ip(ip0);
- port_cfg0.set_def_gw(ip1);
- port_cfg0.set_vlan(0);
- port_cfg1.set_ip(ip1);
- port_cfg1.set_def_gw(ip0);
- port_cfg1.set_vlan(0);
-
- set_port_params(0, port_cfg0, mac0, true);
- set_port_params(1, port_cfg1, mac1, true);
+ uint8_t mac2[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd2};
+ uint32_t ip0 = 0x0f000002;
+ uint32_t ip01 = 0x0f000003;
+ uint32_t ip1 = 0x0f000001;
+ uint16_t ipv6_0[8] = {0x1234, 0x5678, 0xabcd, 0x0, 0x0, 0x0, 0x1111, 0x2220};
+ uint16_t ipv6_1[8] = {0x1234, 0x5678, 0xabcd, 0x0, 0x0, 0x0, 0x1111, 0x2221};
+ uint16_t vlan=1;
+ uint8_t port_0 = 0;
+ uint8_t port_1 = 3;
+
+ add_ip(port_0, ip0, vlan, mac0);
+ add_ip(port_0, ip01, vlan, mac1);
+ add_ip(port_0, ipv6_0, vlan, mac1);
+ add_next_hop(port_0, ip1, vlan);
+ add_next_hop(port_0, ipv6_1, vlan);
+
+ add_ip(port_1, ip1, vlan, mac2);
+ add_ip(port_1, ipv6_1, vlan, mac2);
+ add_next_hop(port_1, ip0, vlan);
+ add_next_hop(port_1, ip01, vlan);
+ add_next_hop(port_1, ipv6_0, vlan);
+
dump(stdout);
+ send_grat_arp_all();
resolve_all();
dump(stdout);
- get_mac(0, ip1, found_mac);
- if (memcmp(found_mac, mac1, ETHER_ADDR_LEN)) {
- fprintf(stderr, "Test failed: Could not resolve def gw on port 0\n");
+
+ if (!get_mac(port_0, ip1, vlan, found_mac)) {
+ fprintf(stderr, "Test failed: Could not find %x on port %d\n", ip1, port_0);
+ exit(1);
+ }
+ if (memcmp(found_mac, mac2, ETHER_ADDR_LEN)) {
+ fprintf(stderr, "Test failed: dest %x on port %d badly resolved\n", ip1, port_0);
exit(1);
}
- get_mac(1, ip0, found_mac);
+ if (!get_mac(port_1, ip0, vlan, found_mac)) {
+ fprintf(stderr, "Test failed: Could not find %x on port %d\n", ip0, port_1);
+ exit(1);
+ }
if (memcmp(found_mac, mac0, ETHER_ADDR_LEN)) {
- fprintf(stderr, "Test failed: Could not resolve def gw on port 1\n");
+ fprintf(stderr, "Test failed: dest %x on port %d badly resolved\n", ip0, port_1);
exit(1);
}
diff --git a/src/pre_test.h b/src/pre_test.h
index ad7608a6..14b444cf 100644
--- a/src/pre_test.h
+++ b/src/pre_test.h
@@ -24,6 +24,7 @@
#include <iostream>
#include <common/Network/Packet/Arp.h>
+#include <common/Network/Packet/MacAddress.h>
#include "bp_sim.h"
#include "trex_defs.h"
@@ -39,60 +40,80 @@ class CPreTestStats {
}
};
-class CPretestPortInfo {
+class CPretestOnePortInfo {
friend class CPretest;
-
- private:
- enum CPretestPortInfoStates {
- INIT_NEEDED,
+ enum CPretestOnePortInfoStates {
RESOLVE_NEEDED,
- RESOLVE_DONE,
RESOLVE_NOT_NEEDED,
};
- CPretestPortInfo() {
- m_state = INIT_NEEDED;
- m_is_loopback = false;
- m_stats.clear();
- }
- void dump(FILE *fd);
- uint8_t *create_arp_req(uint16_t &pkt_size, uint8_t port, bool is_grat);
- void set_params(CPerPortIPCfg port_cfg, const uint8_t *src_mac, bool resolve_needed);
- void set_dst_mac(const uint8_t *dst_mac);
-
+ public:
+ CPretestOnePortInfo();
+ ~CPretestOnePortInfo();
+ void add_src(uint32_t ip, uint16_t vlan, MacAddress mac);
+ void add_dst(uint32_t ip, uint16_t vlan);
+ void add_src(uint16_t ip[8], uint16_t vlan, MacAddress mac);
+ void add_dst(uint16_t ip[8], uint16_t vlan);
+ bool get_mac(uint32_t ip, uint16_t vlan, uint8_t *mac);
+ bool get_mac(uint16_t ip[8], uint16_t vlan, uint8_t *mac);
+ bool get_mac(COneIPInfo *ip, uint8_t *mac);
+ COneIPInfo *get_src(uint16_t vlan, uint8_t ip_ver);
+ void set_port_id(uint16_t port_id) {m_port_id = port_id;}
+ void dump(FILE *fd, char *offset);
+ bool is_loopback() {return m_is_loopback;}
+ CPreTestStats get_stats() {return m_stats;}
+ bool resolve_needed();
+ void send_grat_arp_all();
+ void send_arp_req_all();
+
+ private:
+ COneIPv4Info *find_ip(uint32_t ip, uint16_t vlan);
+ COneIPv4Info *find_next_hop(uint32_t ip, uint16_t vlan);
+ COneIPv6Info *find_ipv6(uint16_t *ip, uint16_t vlan);
+ bool get_mac(COneIPInfo *ip, uint16_t vlan, uint8_t *mac, uint8_t ip_ver);
+
private:
- uint32_t m_ip;
- uint32_t m_def_gw;
- uint16_t m_vlan;
- uint8_t m_src_mac[6];
- uint8_t m_dst_mac[6];
- enum CPretestPortInfoStates m_state;
bool m_is_loopback;
+ CPretestOnePortInfoStates m_state;
CPreTestStats m_stats;
+ uint16_t m_port_id;
+ std::vector<COneIPInfo *> m_src_info;
+ std::vector<COneIPInfo *> m_dst_info;
};
-
class CPretest {
public:
CPretest(uint16_t max_ports) {
m_max_ports = max_ports;
+ for (int i =0; i < max_ports; i++) {
+ m_port_info[i].set_port_id(i);
+ }
}
- bool get_mac(uint16_t port, uint32_t ip, uint8_t *mac);
+ void add_ip(uint16_t port, uint32_t ip, uint16_t vlan, MacAddress src_mac);
+ void add_ip(uint16_t port, uint32_t ip, MacAddress src_mac);
+ void add_next_hop(uint16_t port, uint32_t ip, uint16_t vlan);
+ void add_next_hop(uint16_t port, uint32_t ip);
+ void add_ip(uint16_t port, uint16_t ip[8], uint16_t vlan, MacAddress src_mac);
+ void add_ip(uint16_t port, uint16_t ip[8], MacAddress src_mac);
+ void add_next_hop(uint16_t port, uint16_t ip[8], uint16_t vlan);
+ void add_next_hop(uint16_t port, uint16_t ip[8]);
+ bool get_mac(uint16_t port, uint32_t ip, uint16_t vlan, uint8_t *mac);
+ bool get_mac(uint16_t port, uint16_t ip[8], uint16_t vlan, uint8_t *mac);
CPreTestStats get_stats(uint16_t port_id);
bool is_loopback(uint16_t port);
- void set_port_params(uint16_t port_id, const CPerPortIPCfg &port_cfg, const uint8_t *src_mac, bool resolve_needed);
bool resolve_all();
- void send_arp_req(uint16_t port, bool is_grat);
+ void send_arp_req_all();
void send_grat_arp_all();
- bool is_arp(const uint8_t *p, uint16_t pkt_size, ArpHdr *&arp);
+ bool is_arp(const uint8_t *p, uint16_t pkt_size, ArpHdr *&arp, uint16_t &vlan_tag);
+ void get_results(CManyIPInfo &resolved_ips);
void dump(FILE *fd);
void test();
-
+
private:
int handle_rx(int port, int queue_id);
private:
- CPretestPortInfo m_port_info[TREX_MAX_PORTS];
+ CPretestOnePortInfo m_port_info[TREX_MAX_PORTS];
uint16_t m_max_ports;
};
diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp
index 109cc1a4..d4854a79 100644
--- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp
+++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp
@@ -27,6 +27,8 @@ limitations under the License.
#include <internal_api/trex_platform_api.h>
+#include "trex_stateless_rx_core.h"
+
#include <fstream>
#include <iostream>
#include <unistd.h>
@@ -289,19 +291,15 @@ TrexRpcCmdGetSysInfo::_run(const Json::Value &params, Json::Value &result) {
section["ports"] = Json::arrayValue;
for (int i = 0; i < main->get_port_count(); i++) {
- uint32_t speed;
string driver;
- string hw_macaddr;
- string src_macaddr;
- string dst_macaddr;
string pci_addr;
string description;
supp_speeds_t supp_speeds;
int numa;
TrexStatelessPort *port = main->get_port_by_id(i);
- port->get_properties(driver, speed);
- port->get_macaddr(hw_macaddr, src_macaddr, dst_macaddr);
+
+ port->get_properties(driver);
port->get_pci_info(pci_addr, numa);
main->get_platform_api()->getPortAttrObj(i)->get_description(description);
@@ -311,9 +309,6 @@ TrexRpcCmdGetSysInfo::_run(const Json::Value &params, Json::Value &result) {
section["ports"][i]["driver"] = driver;
section["ports"][i]["description"] = description;
- section["ports"][i]["hw_macaddr"] = hw_macaddr;
- section["ports"][i]["src_macaddr"] = src_macaddr;
- section["ports"][i]["dst_macaddr"] = dst_macaddr;
section["ports"][i]["pci_addr"] = pci_addr;
section["ports"][i]["numa"] = numa;
@@ -330,7 +325,6 @@ TrexRpcCmdGetSysInfo::_run(const Json::Value &params, Json::Value &result) {
section["ports"][i]["rx"]["caps"].append("rx_bytes");
}
section["ports"][i]["rx"]["counters"] = port->get_rx_count_num();
- section["ports"][i]["speed"] = (uint16_t) speed / 1000;
section["ports"][i]["is_fc_supported"] = get_stateless_obj()->get_platform_api()->getPortAttrObj(i)->is_fc_change_supported();
section["ports"][i]["is_led_supported"] = get_stateless_obj()->get_platform_api()->getPortAttrObj(i)->is_led_change_supported();
section["ports"][i]["is_link_supported"] = get_stateless_obj()->get_platform_api()->getPortAttrObj(i)->is_link_change_supported();
@@ -345,6 +339,24 @@ TrexRpcCmdGetSysInfo::_run(const Json::Value &params, Json::Value &result) {
return (TREX_RPC_CMD_OK);
}
+
+int
+TrexRpcCmdSetPortAttr::parse_rx_filter_mode(const Json::Value &msg, uint8_t port_id, Json::Value &result) {
+ const std::string type = parse_choice(msg, "mode", {"hw", "all"}, result);
+
+ rx_filter_mode_e filter_mode;
+ if (type == "hw") {
+ filter_mode = RX_FILTER_MODE_HW;
+ } else if (type == "all") {
+ filter_mode = RX_FILTER_MODE_ALL;
+ } else {
+ /* can't happen - parsed choice */
+ assert(0);
+ }
+
+ return get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->set_rx_filter_mode(filter_mode);
+}
+
/**
* set port commands
*
@@ -361,46 +373,54 @@ TrexRpcCmdSetPortAttr::_run(const Json::Value &params, Json::Value &result) {
uint8_t port_id = parse_port(params, result);
const Json::Value &attr = parse_object(params, "attr", result);
+
int ret = 0;
- bool changed = false;
+
/* iterate over all attributes in the dict */
for (const std::string &name : attr.getMemberNames()) {
+
if (name == "promiscuous") {
bool enabled = parse_bool(attr[name], "enabled", result);
ret = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->set_promiscuous(enabled);
}
+
else if (name == "link_status") {
bool up = parse_bool(attr[name], "up", result);
ret = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->set_link_up(up);
}
+
else if (name == "led_status") {
bool on = parse_bool(attr[name], "on", result);
ret = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->set_led(on);
- } else if (name == "flow_ctrl_mode") {
+ }
+
+ else if (name == "flow_ctrl_mode") {
int mode = parse_int(attr[name], "mode", result);
ret = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->set_flow_ctrl(mode);
- } else {
- generate_execute_err(result, "Not recognized attribute: " + name);
- break;
}
- if (ret != 0){
- if ( ret == -ENOTSUP ) {
- generate_execute_err(result, "Error applying " + name + ": operation is not supported for this NIC.");
- }
- else if (ret) {
- generate_execute_err(result, "Error applying " + name + " attribute, return value: " + to_string(ret));
- }
+
+ else if (name == "rx_filter_mode") {
+ const Json::Value &rx = parse_object(attr, name, result);
+ ret = parse_rx_filter_mode(rx, port_id, result);
+ }
+
+ /* unknown attribute */
+ else {
+ generate_execute_err(result, "unknown attribute type: '" + name + "'");
break;
- } else {
- changed = true;
}
- }
- if (changed) {
- get_stateless_obj()->get_platform_api()->publish_async_port_attr_changed(port_id);
- }
+ /* check error code */
+ if ( ret == -ENOTSUP ) {
+ generate_execute_err(result, "Error applying " + name + ": operation is not supported for this NIC.");
+ } else if (ret) {
+ generate_execute_err(result, "Error applying " + name + " attribute, return value: " + to_string(ret));
+ }
+ }
+
result["result"] = Json::objectValue;
return (TREX_RPC_CMD_OK);
+
}
@@ -568,18 +588,17 @@ TrexRpcCmdGetPortStatus::_run(const Json::Value &params, Json::Value &result) {
result["result"]["owner"] = (port->get_owner().is_free() ? "" : port->get_owner().get_name());
result["result"]["state"] = port->get_state_as_string();
result["result"]["max_stream_id"] = port->get_max_stream_id();
- result["result"]["speed"] = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->get_link_speed();
/* attributes */
- result["result"]["attr"]["promiscuous"]["enabled"] = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->get_promiscuous();
- result["result"]["attr"]["link"]["up"] = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->is_link_up();
- int mode;
- int ret = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->get_flow_ctrl(mode);
- if (ret != 0) {
- mode = -1;
+ get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->to_json(result["result"]["attr"]);
+
+ /* RX info */
+ try {
+ result["result"]["rx_info"] = port->rx_features_to_json();
+ } catch (const TrexException &ex) {
+ generate_execute_err(result, ex.what());
}
- result["result"]["attr"]["fc"]["mode"] = mode;
-
+
return (TREX_RPC_CMD_OK);
}
@@ -612,7 +631,8 @@ TrexRpcCmdPushRemote::_run(const Json::Value &params, Json::Value &result) {
uint8_t port_id = parse_port(params, result);
std::string pcap_filename = parse_string(params, "pcap_filename", result);
double ipg_usec = parse_double(params, "ipg_usec", result);
- double speedup = parse_double(params, "speedup", result);
+ double min_ipg_sec = usec_to_sec(parse_udouble(params, "min_ipg_usec", result, 0));
+ double speedup = parse_udouble(params, "speedup", result);
uint32_t count = parse_uint32(params, "count", result);
double duration = parse_double(params, "duration", result);
bool is_dual = parse_bool(params, "is_dual", result, false);
@@ -630,7 +650,7 @@ TrexRpcCmdPushRemote::_run(const Json::Value &params, Json::Value &result) {
try {
- port->push_remote(pcap_filename, ipg_usec, speedup, count, duration, is_dual);
+ port->push_remote(pcap_filename, ipg_usec, min_ipg_sec, speedup, count, duration, is_dual);
} catch (const TrexException &ex) {
generate_execute_err(result, ex.what());
}
@@ -640,3 +660,210 @@ TrexRpcCmdPushRemote::_run(const Json::Value &params, Json::Value &result) {
}
+/**
+ * set on/off RX software receive mode
+ *
+ */
+trex_rpc_cmd_rc_e
+TrexRpcCmdSetRxFeature::_run(const Json::Value &params, Json::Value &result) {
+
+ uint8_t port_id = parse_port(params, result);
+ TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id);
+
+ /* decide which feature is being set */
+ const std::string type = parse_choice(params, "type", {"capture", "queue", "server"}, result);
+
+ if (type == "capture") {
+ parse_capture_msg(params, port, result);
+ } else if (type == "queue") {
+ parse_queue_msg(params, port, result);
+ } else if (type == "server") {
+ parse_server_msg(params, port, result);
+ } else {
+ assert(0);
+ }
+
+ result["result"] = Json::objectValue;
+ return (TREX_RPC_CMD_OK);
+
+}
+
+void
+TrexRpcCmdSetRxFeature::parse_capture_msg(const Json::Value &msg, TrexStatelessPort *port, Json::Value &result) {
+
+ bool enabled = parse_bool(msg, "enabled", result);
+
+ if (enabled) {
+
+ std::string pcap_filename = parse_string(msg, "pcap_filename", result);
+ uint64_t limit = parse_uint32(msg, "limit", result);
+
+ if (limit == 0) {
+ generate_parse_err(result, "limit cannot be zero");
+ }
+
+ try {
+ port->start_rx_capture(pcap_filename, limit);
+ } catch (const TrexException &ex) {
+ generate_execute_err(result, ex.what());
+ }
+
+ } else {
+
+ try {
+ port->stop_rx_capture();
+ } catch (const TrexException &ex) {
+ generate_execute_err(result, ex.what());
+ }
+
+ }
+
+}
+
+void
+TrexRpcCmdSetRxFeature::parse_queue_msg(const Json::Value &msg, TrexStatelessPort *port, Json::Value &result) {
+ bool enabled = parse_bool(msg, "enabled", result);
+
+ if (enabled) {
+
+ uint64_t size = parse_uint32(msg, "size", result);
+
+ if (size == 0) {
+ generate_parse_err(result, "queue size cannot be zero");
+ }
+
+ try {
+ port->start_rx_queue(size);
+ } catch (const TrexException &ex) {
+ generate_execute_err(result, ex.what());
+ }
+
+ } else {
+
+ try {
+ port->stop_rx_queue();
+ } catch (const TrexException &ex) {
+ generate_execute_err(result, ex.what());
+ }
+
+ }
+
+}
+
+void
+TrexRpcCmdSetRxFeature::parse_server_msg(const Json::Value &msg, TrexStatelessPort *port, Json::Value &result) {
+}
+
+
+trex_rpc_cmd_rc_e
+TrexRpcCmdGetRxQueuePkts::_run(const Json::Value &params, Json::Value &result) {
+
+ uint8_t port_id = parse_port(params, result);
+
+ TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id);
+
+ try {
+ const RXPacketBuffer *pkt_buffer = port->get_rx_queue_pkts();
+ if (pkt_buffer) {
+ result["result"]["pkts"] = pkt_buffer->to_json();
+ delete pkt_buffer;
+
+ } else {
+ result["result"]["pkts"] = Json::arrayValue;
+ }
+
+ } catch (const TrexException &ex) {
+ generate_execute_err(result, ex.what());
+ }
+
+
+ return (TREX_RPC_CMD_OK);
+}
+
+
+/**
+ * configures a port in L2 mode
+ *
+ */
+trex_rpc_cmd_rc_e
+TrexRpcCmdSetL2::_run(const Json::Value &params, Json::Value &result) {
+ uint8_t port_id = parse_port(params, result);
+
+ TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id);
+
+ const std::string dst_mac_str = parse_string(params, "dst_mac", result);
+
+ uint8_t dst_mac[6];
+ if (!utl_str_to_macaddr(dst_mac_str, dst_mac)) {
+ std::stringstream ss;
+ ss << "'invalid MAC address: '" << dst_mac_str << "'";
+ generate_parse_err(result, ss.str());
+ }
+
+ try {
+ port->set_l2_mode(dst_mac);
+ } catch (const TrexException &ex) {
+ generate_execute_err(result, ex.what());
+ }
+
+ return (TREX_RPC_CMD_OK);
+}
+
+/**
+ * configures a port in L3 mode
+ *
+ */
+trex_rpc_cmd_rc_e
+TrexRpcCmdSetL3::_run(const Json::Value &params, Json::Value &result) {
+ uint8_t port_id = parse_port(params, result);
+
+ TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id);
+
+ const std::string src_ipv4_str = parse_string(params, "src_addr", result);
+ const std::string dst_ipv4_str = parse_string(params, "dst_addr", result);
+
+ uint32_t src_ipv4;
+ if (!utl_ipv4_to_uint32(src_ipv4_str.c_str(), src_ipv4)) {
+ std::stringstream ss;
+ ss << "invalid source IPv4 address: '" << src_ipv4_str << "'";
+ generate_parse_err(result, ss.str());
+ }
+
+ uint32_t dst_ipv4;
+ if (!utl_ipv4_to_uint32(dst_ipv4_str.c_str(), dst_ipv4)) {
+ std::stringstream ss;
+ ss << "invalid destination IPv4 address: '" << dst_ipv4_str << "'";
+ generate_parse_err(result, ss.str());
+ }
+
+
+
+ /* did we get a resolved MAC as well ? */
+ if (params["resolved_mac"] != Json::Value::null) {
+ const std::string resolved_mac = parse_string(params, "resolved_mac", result);
+
+ uint8_t mac[6];
+ if (!utl_str_to_macaddr(resolved_mac, mac)) {
+ std::stringstream ss;
+ ss << "'invalid MAC address: '" << resolved_mac << "'";
+ generate_parse_err(result, ss.str());
+ }
+
+ try {
+ port->set_l3_mode(src_ipv4, dst_ipv4, mac);
+ } catch (const TrexException &ex) {
+ generate_execute_err(result, ex.what());
+ }
+
+ } else {
+ try {
+ port->set_l3_mode(src_ipv4, dst_ipv4);
+ } catch (const TrexException &ex) {
+ generate_execute_err(result, ex.what());
+ }
+
+ }
+
+ return (TREX_RPC_CMD_OK);
+
+}
diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp
index c950e011..3f73a5d7 100644
--- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp
+++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp
@@ -40,7 +40,7 @@ TrexRpcCmdAddStream::_run(const Json::Value &params, Json::Value &result) {
uint8_t port_id = parse_port(params, result);
- uint32_t stream_id = parse_int(params, "stream_id", result);
+ uint32_t stream_id = parse_uint32(params, "stream_id", result);
const Json::Value &section = parse_object(params, "stream", result);
@@ -62,7 +62,7 @@ TrexRpcCmdAddStream::_run(const Json::Value &params, Json::Value &result) {
stream->m_random_seed = parse_uint32(section, "random_seed", result,0); /* default is zero */
/* inter stream gap */
- stream->m_isg_usec = parse_double(section, "isg", result);
+ stream->m_isg_usec = parse_udouble(section, "isg", result);
stream->m_next_stream_id = parse_int(section, "next_stream_id", result);
@@ -114,7 +114,7 @@ TrexRpcCmdAddStream::_run(const Json::Value &params, Json::Value &result) {
generate_parse_err(result, "RX stats is not supported on this interface");
}
- stream->m_rx_check.m_pg_id = parse_int(rx, "stream_id", result);
+ stream->m_rx_check.m_pg_id = parse_uint32(rx, "stream_id", result);
std::string type = parse_string(rx, "rule_type", result);
if (type == "latency") {
stream->m_rx_check.m_rule_type = TrexPlatformApi::IF_STAT_PAYLOAD;
@@ -155,7 +155,7 @@ TrexRpcCmdAddStream::allocate_new_stream(const Json::Value &section, uint8_t por
} else if (type == "single_burst") {
- uint32_t total_pkts = parse_int(mode, "total_pkts", result);
+ uint32_t total_pkts = parse_uint32(mode, "total_pkts", result);
stream.reset(new TrexStream(TrexStream::stSINGLE_BURST, port_id, stream_id));
stream->set_single_burst(total_pkts);
@@ -163,9 +163,9 @@ TrexRpcCmdAddStream::allocate_new_stream(const Json::Value &section, uint8_t por
} else if (type == "multi_burst") {
- double ibg_usec = parse_double(mode, "ibg", result);
- uint32_t num_bursts = parse_int(mode, "count", result);
- uint32_t pkts_per_burst = parse_int(mode, "pkts_per_burst", result);
+ double ibg_usec = parse_udouble(mode, "ibg", result);
+ uint32_t num_bursts = parse_uint32(mode, "count", result);
+ uint32_t pkts_per_burst = parse_uint32(mode, "pkts_per_burst", result);
stream.reset(new TrexStream(TrexStream::stMULTI_BURST,port_id, stream_id ));
stream->set_multi_burst(pkts_per_burst,num_bursts,ibg_usec);
@@ -186,12 +186,7 @@ TrexRpcCmdAddStream::allocate_new_stream(const Json::Value &section, uint8_t por
void
TrexRpcCmdAddStream::parse_rate(const Json::Value &rate, std::unique_ptr<TrexStream> &stream, Json::Value &result) {
- double value = parse_double(rate, "value", result);
- if (value <= 0) {
- std::stringstream ss;
- ss << "rate value must be a positive number - got: '" << value << "'";
- generate_parse_err(result, ss.str());
- }
+ double value = parse_udouble(rate, "value", result);
auto rate_types = {"pps", "bps_L1", "bps_L2", "percentage"};
std::string rate_type = parse_choice(rate, "type", rate_types, result);
@@ -533,7 +528,7 @@ TrexRpcCmdRemoveStream::_run(const Json::Value &params, Json::Value &result) {
uint8_t port_id = parse_port(params, result);
TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id);
- uint32_t stream_id = parse_int(params, "stream_id", result);
+ uint32_t stream_id = parse_uint32(params, "stream_id", result);
TrexStream *stream = port->get_stream_by_id(stream_id);
if (!stream) {
@@ -615,7 +610,7 @@ TrexRpcCmdGetStream::_run(const Json::Value &params, Json::Value &result) {
TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(port_id);
bool get_pkt = parse_bool(params, "get_pkt", result);
- uint32_t stream_id = parse_int(params, "stream_id", result);
+ uint32_t stream_id = parse_uint32(params, "stream_id", result);
TrexStream *stream = port->get_stream_by_id(stream_id);
@@ -660,9 +655,9 @@ TrexRpcCmdStartTraffic::_run(const Json::Value &params, Json::Value &result) {
std::string type = parse_choice(mul_obj, "type", TrexPortMultiplier::g_types, result);
std::string op = parse_string(mul_obj, "op", result);
- double value = parse_double(mul_obj, "value", result);
+ double value = parse_udouble(mul_obj, "value", result);
- if ( value <=0 ){
+ if ( value == 0 ){
generate_parse_err(result, "multiplier can't be zero");
}
@@ -670,6 +665,7 @@ TrexRpcCmdStartTraffic::_run(const Json::Value &params, Json::Value &result) {
generate_parse_err(result, "start message can only specify absolute speed rate");
}
+ dsec_t ts = now_sec();
TrexPortMultiplier mul(type, op, value);
try {
@@ -680,7 +676,8 @@ TrexRpcCmdStartTraffic::_run(const Json::Value &params, Json::Value &result) {
}
result["result"]["multiplier"] = port->get_multiplier();
-
+ result["result"]["ts"] = ts;
+
return (TREX_RPC_CMD_OK);
}
diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h
index 5fde1d0c..6639be7b 100644
--- a/src/rpc-server/commands/trex_rpc_cmds.h
+++ b/src/rpc-server/commands/trex_rpc_cmds.h
@@ -27,6 +27,7 @@ limitations under the License.
#include <memory>
class TrexStream;
+class TrexStatelessPort;
/* all the RPC commands decl. goes here */
@@ -89,10 +90,15 @@ TREX_RPC_CMD_DEFINE(TrexRpcCmdRelease, "release", 1, true, APIClass:
*/
TREX_RPC_CMD_DEFINE(TrexRpcCmdGetPortStats, "get_port_stats", 1, false, APIClass::API_CLASS_TYPE_CORE);
TREX_RPC_CMD_DEFINE(TrexRpcCmdGetPortStatus, "get_port_status", 1, false, APIClass::API_CLASS_TYPE_CORE);
-TREX_RPC_CMD_DEFINE(TrexRpcCmdSetPortAttr, "set_port_attr", 2, true, APIClass::API_CLASS_TYPE_CORE);
TREX_RPC_CMD_DEFINE(TrexRpcCmdGetPortXStatsValues, "get_port_xstats_values", 1, false, APIClass::API_CLASS_TYPE_CORE);
TREX_RPC_CMD_DEFINE(TrexRpcCmdGetPortXStatsNames, "get_port_xstats_names", 1, false, APIClass::API_CLASS_TYPE_CORE);
+TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdSetPortAttr, "set_port_attr", 2, true, APIClass::API_CLASS_TYPE_CORE,
+
+ int parse_rx_filter_mode(const Json::Value &msg, uint8_t port_id, Json::Value &result);
+);
+
+
/**
* stream cmds
*/
@@ -144,5 +150,16 @@ TREX_RPC_CMD_DEFINE(TrexRpcCmdPushRemote, "push_remote", 6, true, APIClass::API_
TREX_RPC_CMD_DEFINE(TrexRpcCmdShutdown, "shutdown", 2, false, APIClass::API_CLASS_TYPE_CORE);
+TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdSetRxFeature, "set_rx_feature", 3, false, APIClass::API_CLASS_TYPE_CORE,
+ void parse_capture_msg(const Json::Value &msg, TrexStatelessPort *port, Json::Value &result);
+ void parse_queue_msg(const Json::Value &msg, TrexStatelessPort *port, Json::Value &result);
+ void parse_server_msg(const Json::Value &msg, TrexStatelessPort *port, Json::Value &result);
+
+);
+
+TREX_RPC_CMD_DEFINE(TrexRpcCmdSetL2, "set_l2", 2, false, APIClass::API_CLASS_TYPE_CORE);
+TREX_RPC_CMD_DEFINE(TrexRpcCmdSetL3, "set_l3", 3, false, APIClass::API_CLASS_TYPE_CORE);
+TREX_RPC_CMD_DEFINE(TrexRpcCmdGetRxQueuePkts, "get_rx_queue_pkts", 2, false, APIClass::API_CLASS_TYPE_CORE);
+
#endif /* __TREX_RPC_CMD_H__ */
diff --git a/src/rpc-server/trex_rpc_cmd.cpp b/src/rpc-server/trex_rpc_cmd.cpp
index 265d426b..6c56a59f 100644
--- a/src/rpc-server/trex_rpc_cmd.cpp
+++ b/src/rpc-server/trex_rpc_cmd.cpp
@@ -153,6 +153,8 @@ TrexRpcCommand::type_to_str(field_type_e type) {
return "int";
case FIELD_TYPE_DOUBLE:
return "double";
+ case FIELD_TYPE_UDOUBLE:
+ return "unsigned double";
case FIELD_TYPE_OBJ:
return "object";
case FIELD_TYPE_STR:
@@ -176,7 +178,7 @@ TrexRpcCommand::json_type_to_name(const Json::Value &value) {
case Json::uintValue:
return "uint";
case Json::realValue:
- return "real";
+ return "double";
case Json::stringValue:
return "string";
case Json::booleanValue:
@@ -223,31 +225,41 @@ TrexRpcCommand::check_field_type(const Json::Value &parent, const std::string &n
void
TrexRpcCommand::check_field_type_common(const Json::Value &field, const std::string &name, field_type_e type, Json::Value &result) {
- std::stringstream ss;
+ std::string specific_err;
/* first check if field exists */
if (field == Json::Value::null) {
- ss << "field '" << name << "' is missing";
- generate_parse_err(result, ss.str());
+ specific_err = "field '" + name + "' is missing";
+ generate_parse_err(result, specific_err);
}
bool rc = true;
+ specific_err = "is '" + std::string(json_type_to_name(field)) + "', expecting '" + std::string(type_to_str(type)) + "'";
switch (type) {
case FIELD_TYPE_BYTE:
- if ( (!field.isUInt()) || (field.asInt() > 0xFF)) {
+ if (!field.isUInt64()) {
+ rc = false;
+ } else if (field.asUInt64() > 0xFF) {
+ specific_err = "has size bigger than uint8.";
rc = false;
}
break;
case FIELD_TYPE_UINT16:
- if ( (!field.isUInt()) || (field.asInt() > 0xFFFF)) {
+ if (!field.isUInt64()) {
+ rc = false;
+ } else if (field.asUInt64() > 0xFFFF) {
+ specific_err = "has size bigger than uint16.";
rc = false;
}
break;
case FIELD_TYPE_UINT32:
- if ( (!field.isUInt()) || (field.asUInt() > 0xFFFFFFFF)) {
+ if (!field.isUInt64()) {
+ rc = false;
+ } else if (field.asUInt64() > 0xFFFFFFFF) {
+ specific_err = "has size bigger than uint32.";
rc = false;
}
break;
@@ -276,6 +288,15 @@ TrexRpcCommand::check_field_type_common(const Json::Value &field, const std::str
}
break;
+ case FIELD_TYPE_UDOUBLE:
+ if (!field.isDouble()) {
+ rc = false;
+ } else if (field.asDouble() < 0) {
+ specific_err = "has negative value.";
+ rc = false;
+ }
+ break;
+
case FIELD_TYPE_OBJ:
if (!field.isObject()) {
rc = false;
@@ -300,8 +321,7 @@ TrexRpcCommand::check_field_type_common(const Json::Value &field, const std::str
}
if (!rc) {
- ss << "error at offset: " << field.getOffsetStart() << " - '" << name << "' is '" << json_type_to_name(field) << "', expecting '" << type_to_str(type) << "'";
- generate_parse_err(result, ss.str());
+ generate_parse_err(result, "error at offset: " + std::to_string(field.getOffsetStart()) + " - '" + name + "' " + specific_err);
}
}
diff --git a/src/rpc-server/trex_rpc_cmd_api.h b/src/rpc-server/trex_rpc_cmd_api.h
index de0f5b58..2536f69c 100644
--- a/src/rpc-server/trex_rpc_cmd_api.h
+++ b/src/rpc-server/trex_rpc_cmd_api.h
@@ -114,6 +114,7 @@ protected:
FIELD_TYPE_UINT64,
FIELD_TYPE_INT,
FIELD_TYPE_DOUBLE,
+ FIELD_TYPE_UDOUBLE,
FIELD_TYPE_BOOL,
FIELD_TYPE_STR,
FIELD_TYPE_OBJ,
@@ -184,6 +185,11 @@ protected:
return parent[param].asDouble();
}
+ template<typename T> double parse_udouble(const Json::Value &parent, const T &param, Json::Value &result) {
+ check_field_type(parent, param, FIELD_TYPE_UDOUBLE, result);
+ return parent[param].asDouble();
+ }
+
template<typename T> bool parse_bool(const Json::Value &parent, const T &param, Json::Value &result) {
check_field_type(parent, param, FIELD_TYPE_BOOL, result);
return parent[param].asBool();
@@ -256,6 +262,20 @@ protected:
return parse_double(parent, param, result);
}
+ template<typename T> double parse_udouble(const Json::Value &parent, const T &param, Json::Value &result, double def) {
+ /* if not exists - default */
+ if (parent[param] == Json::Value::null) {
+ if (def < 0) {
+ std::stringstream ss;
+ ss << "default value of '" << param << "' is negative (please report)";
+ generate_parse_err(result, ss.str());
+ } else {
+ return def;
+ }
+ }
+ return parse_udouble(parent, param, result);
+ }
+
template<typename T> bool parse_bool(const Json::Value &parent, const T &param, Json::Value &result, bool def) {
/* if not exists - default */
if (parent[param] == Json::Value::null) {
diff --git a/src/rpc-server/trex_rpc_cmds_table.cpp b/src/rpc-server/trex_rpc_cmds_table.cpp
index cddf19b9..94a3e1b9 100644
--- a/src/rpc-server/trex_rpc_cmds_table.cpp
+++ b/src/rpc-server/trex_rpc_cmds_table.cpp
@@ -71,6 +71,12 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() {
register_command(new TrexRpcCmdPushRemote());
register_command(new TrexRpcCmdShutdown());
+
+ register_command(new TrexRpcCmdSetRxFeature());
+ register_command(new TrexRpcCmdGetRxQueuePkts());
+
+ register_command(new TrexRpcCmdSetL2());
+ register_command(new TrexRpcCmdSetL3());
}
diff --git a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp
index 4fa2447d..d08de4e1 100644
--- a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp
+++ b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp
@@ -235,3 +235,12 @@ TrexJsonRpcV2Parser::generate_common_error(Json::Value &json, const std::string
}
+void
+TrexJsonRpcV2Parser::generate_common_error(std::string &response, const std::string &specific_err) {
+ Json::Value resp_json;
+ Json::FastWriter writer;
+
+ generate_common_error(resp_json, specific_err);
+ response = writer.write(resp_json);
+}
+
diff --git a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h
index 0563f21d..d91cbe2d 100644
--- a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h
+++ b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h
@@ -89,6 +89,15 @@ public:
static void generate_common_error(Json::Value &json, const std::string &specific_err);
/**
+ * will generate a valid JSON RPC v2 error message with
+ * generic error code and message
+ *
+ * @author imarom (16-Sep-15)
+ *
+ */
+ static void generate_common_error(std::string &response, const std::string &specific_err);
+
+ /**
* *tries* to generate a pretty string from JSON
* if json_str is not a valid JSON string
* it will duplicate the source
diff --git a/src/rpc-server/trex_rpc_req_resp_server.cpp b/src/rpc-server/trex_rpc_req_resp_server.cpp
index 28bf1d80..e762b8c1 100644
--- a/src/rpc-server/trex_rpc_req_resp_server.cpp
+++ b/src/rpc-server/trex_rpc_req_resp_server.cpp
@@ -171,7 +171,12 @@ void TrexRpcServerReqRes::_stop_rpc_thread() {
void TrexRpcServerReqRes::handle_request(const std::string &request) {
std::string response;
- process_request(request, response);
+ if ( request.size() > MAX_RPC_MSG_LEN ) {
+ std::string err_msg = "Request is too large (" + std::to_string(request.size()) + " bytes). Consider splitting to smaller chunks.";
+ TrexJsonRpcV2Parser::generate_common_error(response, err_msg);
+ } else {
+ process_request(request, response);
+ }
zmq_send(m_socket, response.c_str(), response.size(), 0);
}
@@ -244,7 +249,12 @@ void TrexRpcServerReqRes::process_zipped_request(const std::string &request, std
/* process the request */
std::string raw_response;
- process_request_raw(unzipped, raw_response);
+ if ( unzipped.size() > MAX_RPC_MSG_LEN ) {
+ std::string err_msg = "Request is too large (" + std::to_string(unzipped.size()) + " bytes). Consider splitting to smaller chunks.";
+ TrexJsonRpcV2Parser::generate_common_error(raw_response, err_msg);
+ } else {
+ process_request_raw(unzipped, raw_response);
+ }
TrexRpcZip::compress(raw_response, response);
@@ -256,18 +266,14 @@ void TrexRpcServerReqRes::process_zipped_request(const std::string &request, std
*/
void
TrexRpcServerReqRes::handle_server_error(const std::string &specific_err) {
- Json::FastWriter writer;
- Json::Value response;
+ std::string response;
/* generate error */
TrexJsonRpcV2Parser::generate_common_error(response, specific_err);
- /* write the JSON to string and sever on ZMQ */
- std::string response_str = writer.write(response);
-
- verbose_json("Server Replied: ", response_str);
+ verbose_json("Server Replied: ", response);
- zmq_send(m_socket, response_str.c_str(), response_str.size(), 0);
+ zmq_send(m_socket, response.c_str(), response.size(), 0);
}
diff --git a/src/rpc-server/trex_rpc_req_resp_server.h b/src/rpc-server/trex_rpc_req_resp_server.h
index 92d51a2a..9a994044 100644
--- a/src/rpc-server/trex_rpc_req_resp_server.h
+++ b/src/rpc-server/trex_rpc_req_resp_server.h
@@ -53,6 +53,7 @@ protected:
void *m_context;
void *m_socket;
+ static const uint32_t MAX_RPC_MSG_LEN = 999999;
};
/**
diff --git a/src/rx_check.cpp b/src/rx_check.cpp
index bfaa4ddb..d7eecede 100755
--- a/src/rx_check.cpp
+++ b/src/rx_check.cpp
@@ -255,7 +255,9 @@ bool RxCheckManager::Create(){
void RxCheckManager::handle_packet(CRx_check_header * rxh){
- //rxh->dump(stdout);
+ // m_stats.Dump(stdout);
+ //rxh->dump(stdout);
+
m_stats.m_total_rx++;
if ( rxh->m_magic != RX_CHECK_MAGIC ){
m_stats.m_err_no_magic++;
diff --git a/src/stateful_rx_core.cpp b/src/stateful_rx_core.cpp
index ebc51fcb..dced7360 100644
--- a/src/stateful_rx_core.cpp
+++ b/src/stateful_rx_core.cpp
@@ -33,7 +33,7 @@ const uint8_t sctp_pkt[]={
0x00,0x0e,0x2e,0x24,0x37,0x5f,
0x08,0x00,
- 0x45,0x02,0x00,0x30,
+ 0x45,0x03,0x00,0x30,
0x00,0x00,0x40,0x00,
0xff,0x84,0xbd,0x04,
0x9b,0xe6,0x18,0x9b, //sIP
@@ -57,7 +57,7 @@ const uint8_t icmp_pkt[]={
0x00,0x0e,0x2e,0x24,0x37,0x5f,
0x08,0x00,
- 0x45,0x02,0x00,0x30,
+ 0x45,0x03,0x00,0x30,
0x00,0x00,0x40,0x00,
0xff,0x01,0xbd,0x04,
0x9b,0xe6,0x18,0x9b, //SIP
@@ -126,32 +126,32 @@ void CLatencyPktInfo::Create(class CLatencyPktMode *m_l_pkt_info){
}
-rte_mbuf_t * CLatencyPktInfo::generate_pkt(int port_id,uint32_t extern_ip){
- bool is_client_to_server=(port_id%2==0)?true:false;
+rte_mbuf_t * CLatencyPktInfo::generate_pkt(int port_id, uint32_t extern_ip) {
+ bool is_client_to_server = (port_id % 2 == 0) ? true:false;
+ int dual_port_index = port_id >> 1;
+ uint32_t mask = dual_port_index * m_dual_port_mask;
+ uint32_t c = m_client_ip.v4;
+ uint32_t s = m_server_ip.v4;
- int dual_port_index=(port_id>>1);
- uint32_t c=m_client_ip.v4;
- uint32_t s=m_server_ip.v4;
- if ( extern_ip ){
- c=extern_ip;
+ if (is_client_to_server) {
+ if ( extern_ip ) {
+ m_dummy_node.m_src_ip = extern_ip;
+ } else {
+ m_dummy_node.m_src_ip = c + mask;
+ }
+ m_dummy_node.m_dest_ip = s + mask;
+ } else {
+ if ( extern_ip ) {
+ m_dummy_node.m_dest_ip = extern_ip;
+ } else {
+ m_dummy_node.m_dest_ip = c + mask;
+ }
+ m_dummy_node.m_src_ip = s + mask;
}
- if (!is_client_to_server) {
- /*swap */
- uint32_t t=c;
- c=s;
- s=t;
- }
- uint32_t mask=dual_port_index*m_dual_port_mask;
- if ( extern_ip==0 ){
- c+=mask;
- }
- s+=mask;
- m_dummy_node.m_src_ip = c;
- m_dummy_node.m_dest_ip = s;
+ rte_mbuf_t *m = m_pkt_info.generate_new_mbuf(&m_dummy_node);
- rte_mbuf_t * m=m_pkt_info.generate_new_mbuf(&m_dummy_node);
- return (m);
+ return m;
}
void CLatencyPktInfo::set_ip(uint32_t src,
@@ -461,8 +461,11 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) {
}
if ( (pkt_size-vlan_offset) != m_pkt_size ) {
- m_length_error++;
- return (false);
+ // patch for e1000 card. e1000 hands us the packet with Ethernet FCS, so it is 4 bytes longer
+ if ((pkt_size - vlan_offset - 4) != m_pkt_size ) {
+ m_length_error++;
+ return (false);
+ }
}
c_l_pkt_mode->update_recv(p + m_l4_offset + vlan_offset, &m_icmp_rx_seq, &m_icmp_tx_seq);
#ifdef LATENCY_DEBUG
@@ -588,32 +591,56 @@ void CLatencyManager::send_pkt_all_ports(){
}
}
-void CLatencyManager::send_grat_arp_all_ports() {
- for (int port_id = 0; port_id < m_max_ports; port_id++) {
- if (! CGlobalInfo::m_options.m_ip_cfg[port_id].grat_arp_needed())
- continue;
-
- CLatencyManagerPerPort * lp = &m_ports[port_id];
- rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc_small(CGlobalInfo::m_socket.port_to_socket(port_id));
- assert(m);
- uint8_t *p = (uint8_t *)rte_pktmbuf_append(m, 60); // ARP packet is shorter than 60
- uint32_t sip = CGlobalInfo::m_options.m_ip_cfg[port_id].get_ip();
- uint8_t *src_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src;
- uint16_t vlan = CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan();
- // gratuitous ARP. Requested IP is our source.
- CTestPktGen::create_arp_req(p, sip, sip, src_mac, vlan, port_id);
+double CLatencyManager::grat_arp_timeout() {
+ return (double)CGlobalInfo::m_options.m_arp_ref_per / m_arp_info.size();
+}
+
+void CLatencyManager::add_grat_arp_src(COneIPv4Info &ip) {
+ m_arp_info.insert(ip);
+}
+void CLatencyManager::send_one_grat_arp() {
+ const COneIPInfo *ip_info;
+ uint16_t port_id;
+ CLatencyManagerPerPort * lp;
+ rte_mbuf_t *m;
+ uint8_t src_mac[ETHER_ADDR_LEN];
+ uint16_t vlan;
+ uint32_t sip;
+
+ ip_info = m_arp_info.get_next();
+ if (!ip_info)
+ ip_info = m_arp_info.get_next();
+ // Two times NULL means there are no addresses
+ if (!ip_info)
+ return;
+
+ port_id = ip_info->get_port();
+ lp = &m_ports[port_id];
+ m = CGlobalInfo::pktmbuf_alloc_small(CGlobalInfo::m_socket.port_to_socket(port_id));
+ assert(m);
+ uint8_t *p = (uint8_t *)rte_pktmbuf_append(m, ip_info->get_grat_arp_len());
+ ip_info->get_mac(src_mac);
+ vlan = ip_info->get_vlan();
+ switch(ip_info->ip_ver()) {
+ case COneIPInfo::IP4_VER:
+ sip = ((COneIPv4Info *)ip_info)->get_ip();
+ CTestPktGen::create_arp_req(p, sip, sip, src_mac, vlan, port_id);
if (CGlobalInfo::m_options.preview.getVMode() >= 3) {
- printf("Sending gratuitous ARP on port %d vlan:%d, sip:0x%08x\n", port_id, vlan, sip);
+ printf("Sending gratuitous ARP on port %d vlan:%d, sip:%s\n", port_id, vlan
+ , ip_to_str(sip).c_str());
utl_DumpBuffer(stdout, p, 60, 0);
}
-
if ( lp->m_io->tx(m) == 0 ) {
lp->m_port.m_ign_stats.m_tx_arp++;
lp->m_port.m_ign_stats.m_tot_bytes += 64; // mbuf size is smaller, but 64 bytes will be sent
} else {
lp->m_port.m_tx_pkt_err++;
}
+ break;
+ case COneIPInfo::IP6_VER:
+ //??? implement ipv6
+ break;
}
}
@@ -653,58 +680,6 @@ void CLatencyManager::handle_rx_pkt(CLatencyManagerPerPort * lp,
rte_pktmbuf_free(m);
}
-// In VM, we receive the RX packets in DP core, and send message to RX core with the packet
-void CLatencyManager::handle_latency_pkt_msg(uint8_t thread_id, CGenNodeLatencyPktInfo * msg) {
-
- assert(msg->m_latency_offset==0xdead);
-
- uint8_t rx_port_index=(thread_id<<1)+(msg->m_dir&1);
- assert( rx_port_index <m_max_ports ) ;
- CLatencyManagerPerPort * lp=&m_ports[rx_port_index];
- handle_rx_pkt(lp,(rte_mbuf_t *)msg->m_pkt);
-}
-
-
-void CLatencyManager::run_rx_queue_msgs(uint8_t thread_id,
- CNodeRing * r){
-
- while ( true ) {
- CGenNode * node;
- if ( r->Dequeue(node)!=0 ){
- break;
- }
- assert(node);
-
- CGenNodeMsgBase * msg=(CGenNodeMsgBase *)node;
-
- uint8_t msg_type = msg->m_msg_type;
- switch (msg_type ) {
- case CGenNodeMsgBase::LATENCY_PKT:
- handle_latency_pkt_msg(thread_id,(CGenNodeLatencyPktInfo *) msg);
- break;
- default:
- printf("ERROR latency-thread message type is not valid %d \n",msg_type);
- assert(0);
- }
-
- CGlobalInfo::free_node(node);
- }
-}
-
-// VM mode function. Handle messages from DP
-void CLatencyManager::try_rx_queues(){
-
- CMessagingManager * rx_dp = CMsgIns::Ins()->getRxDp();
- uint8_t threads=CMsgIns::Ins()->get_num_threads();
- int ti;
- for (ti=0; ti<(int)threads; ti++) {
- CNodeRing * r = rx_dp->getRingDpToCp(ti);
- if ( !r->isEmpty() ){
- run_rx_queue_msgs((uint8_t)ti,r);
- }
- }
-}
-
void CLatencyManager::try_rx(){
rte_mbuf_t * rx_pkts[64];
int i;
@@ -766,8 +741,6 @@ void CLatencyManager::start(int iter, bool activate_watchdog) {
m_p_queue.push(node);
}
- bool do_try_rx_queue = CGlobalInfo::m_options.preview.get_vm_one_queue_enable() ? true : false;
-
if (activate_watchdog) {
m_monitor.create("STF RX CORE", 1);
TrexWatchDog::getInstance().register_monitor(&m_monitor);
@@ -783,9 +756,6 @@ void CLatencyManager::start(int iter, bool activate_watchdog) {
if (dt> (0.0)) {
break;
}
- if (do_try_rx_queue){
- try_rx_queues();
- }
try_rx();
rte_pause();
}
@@ -815,9 +785,9 @@ void CLatencyManager::start(int iter, bool activate_watchdog) {
case CGenNode::GRAT_ARP:
m_cpu_dp_u.start_work1();
- send_grat_arp_all_ports();
+ send_one_grat_arp();
m_p_queue.pop();
- node->m_time += (double)CGlobalInfo::m_options.m_arp_ref_per;
+ node->m_time += grat_arp_timeout();
m_p_queue.push(node);
m_cpu_dp_u.commit1();
break;
diff --git a/src/stateful_rx_core.h b/src/stateful_rx_core.h
index 3fa5892f..8744a58a 100644
--- a/src/stateful_rx_core.h
+++ b/src/stateful_rx_core.h
@@ -24,6 +24,7 @@ limitations under the License.
#include "bp_sim.h"
#include "flow_stat.h"
+#include "utl_ip.h"
#define L_PKT_SUBMODE_NO_REPLY 1
#define L_PKT_SUBMODE_REPLY 2
@@ -31,33 +32,6 @@ limitations under the License.
class TrexWatchDog;
-class CRXCoreIgnoreStat {
- friend class CCPortLatency;
- friend class CLatencyManager;
- public:
- inline CRXCoreIgnoreStat operator- (const CRXCoreIgnoreStat &t_in) {
- CRXCoreIgnoreStat t_out;
- t_out.m_tx_arp = this->m_tx_arp - t_in.m_tx_arp;
- t_out.m_tx_ipv6_n_solic = this->m_tx_ipv6_n_solic - t_in.m_tx_ipv6_n_solic;
- t_out.m_tot_bytes = this->m_tot_bytes - t_in.m_tot_bytes;
- return t_out;
- }
- uint64_t get_tx_bytes() {return m_tot_bytes;}
- uint64_t get_tx_pkts() {return m_tx_arp + m_tx_ipv6_n_solic;}
- uint64_t get_tx_arp() {return m_tx_arp;}
- uint64_t get_tx_n_solic() {return m_tx_ipv6_n_solic;}
- void clear() {
- m_tx_arp = 0;
- m_tx_ipv6_n_solic = 0;
- m_tot_bytes = 0;
- }
-
- private:
- uint64_t m_tx_arp;
- uint64_t m_tx_ipv6_n_solic;
- uint64_t m_tot_bytes;
-};
-
class CLatencyPktInfo {
public:
void Create(class CLatencyPktMode *m_l_pkt_info);
@@ -346,18 +320,16 @@ public:
return ( &m_nat_check_manager );
}
CLatencyPktMode *c_l_pkt_mode;
+ void add_grat_arp_src(COneIPv4Info &ip);
private:
void tickle();
void send_pkt_all_ports();
- void send_grat_arp_all_ports();
+ double grat_arp_timeout();
+ void send_one_grat_arp();
void try_rx();
- void try_rx_queues();
- void run_rx_queue_msgs(uint8_t thread_id, CNodeRing * r);
void wait_for_rx_dump();
void handle_rx_pkt(CLatencyManagerPerPort * lp, rte_mbuf_t * m);
- /* messages handlers */
- void handle_latency_pkt_msg(uint8_t thread_id, CGenNodeLatencyPktInfo * msg);
private:
pqueue_t m_p_queue; /* priorty queue */
@@ -374,7 +346,7 @@ private:
CCpuUtlDp m_cpu_dp_u;
CCpuUtlCp m_cpu_cp_u;
TrexMonitor m_monitor;
-
+ CManyIPInfo m_arp_info; // for grat ARP
volatile bool m_do_stop __rte_cache_aligned ;
};
diff --git a/src/stateless/cp/trex_exception.h b/src/stateless/cp/trex_exception.h
index c12732ef..ffc2f734 100644
--- a/src/stateless/cp/trex_exception.h
+++ b/src/stateless/cp/trex_exception.h
@@ -55,7 +55,10 @@ class TrexException : public std::runtime_error
T_FLOW_STAT_NO_FREE_HW_ID,
T_FLOW_STAT_RX_CORE_START_FAIL,
T_FLOW_STAT_BAD_HW_ID,
- T_INVALID
+
+ T_RX_PKT_PARSE_ERR,
+
+ T_INVALID,
};
TrexException() : std::runtime_error(""), m_type(T_INVALID) {
diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp
index 9bb20990..e9b3c6d4 100644
--- a/src/stateless/cp/trex_stateless_port.cpp
+++ b/src/stateless/cp/trex_stateless_port.cpp
@@ -25,6 +25,7 @@ limitations under the License.
#include <trex_streams_compiler.h>
#include <common/basic_utils.h>
#include <common/captureFile.h>
+#include "trex_stateless_rx_defs.h"
#include <string>
@@ -98,20 +99,26 @@ protected:
************************************/
class StreamsFeeder {
public:
+
StreamsFeeder(TrexStatelessPort *port) {
-
/* start pesimistic */
m_success = false;
-
+
+ m_port = port;
+ }
+
+ void feed() {
+
/* fetch the original streams */
- port->get_object_list(m_in_streams);
+ m_port->get_object_list(m_in_streams);
for (const TrexStream *in_stream : m_in_streams) {
TrexStream *out_stream = in_stream->clone(true);
- get_stateless_obj()->m_rx_flow_stat.start_stream(out_stream);
-
m_out_streams.push_back(out_stream);
+
+ get_stateless_obj()->m_rx_flow_stat.start_stream(out_stream);
+
}
}
@@ -146,6 +153,8 @@ private:
vector<TrexStream *> m_in_streams;
vector<TrexStream *> m_out_streams;
bool m_success;
+
+ TrexStatelessPort *m_port;
};
@@ -156,9 +165,9 @@ private:
TrexStatelessPort::TrexStatelessPort(uint8_t port_id, const TrexPlatformApi *api) : m_dp_events(this) {
std::vector<std::pair<uint8_t, uint8_t>> core_pair_list;
- m_port_id = port_id;
- m_port_state = PORT_STATE_IDLE;
- m_platform_api = api;
+ m_port_id = port_id;
+ m_port_state = PORT_STATE_IDLE;
+ m_platform_api = api;
/* get the platform specific data */
api->get_interface_info(port_id, m_api_info);
@@ -262,6 +271,7 @@ TrexStatelessPort::start_traffic(const TrexPortMultiplier &mul, double duration,
double factor = calculate_effective_factor(mul, force);
StreamsFeeder feeder(this);
+ feeder.feed();
/* compiler it */
std::vector<TrexStreamsCompiledObj *> compiled_objs;
@@ -501,6 +511,7 @@ TrexStatelessPort::update_traffic(const TrexPortMultiplier &mul, bool force) {
void
TrexStatelessPort::push_remote(const std::string &pcap_filename,
double ipg_usec,
+ double min_ipg_sec,
double speedup,
uint32_t count,
double duration,
@@ -540,6 +551,7 @@ TrexStatelessPort::push_remote(const std::string &pcap_filename,
m_pending_async_stop_event,
pcap_filename,
ipg_usec,
+ min_ipg_sec,
speedup,
count,
duration,
@@ -584,10 +596,9 @@ TrexStatelessPort::get_max_stream_id() const {
}
void
-TrexStatelessPort::get_properties(std::string &driver, uint32_t &speed) {
+TrexStatelessPort::get_properties(std::string &driver) {
driver = m_api_info.driver_name;
- speed = m_platform_api->getPortAttrObj(m_port_id)->get_link_speed();
}
bool
@@ -888,16 +899,6 @@ TrexStatelessPort::get_port_effective_rate(double &pps,
}
void
-TrexStatelessPort::get_macaddr(std::string &hw_macaddr,
- std::string &src_macaddr,
- std::string &dst_macaddr) {
-
- utl_macaddr_to_str(m_api_info.mac_info.hw_macaddr, hw_macaddr);
- utl_macaddr_to_str(m_api_info.mac_info.src_macaddr, src_macaddr);
- utl_macaddr_to_str(m_api_info.mac_info.dst_macaddr, dst_macaddr);
-}
-
-void
TrexStatelessPort::get_pci_info(std::string &pci_addr, int &numa_node) {
pci_addr = m_api_info.pci_addr;
numa_node = m_api_info.numa_node;
@@ -908,6 +909,9 @@ TrexStatelessPort::add_stream(TrexStream *stream) {
verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS, "add_stream");
+ if (m_stream_table.size() >= MAX_STREAMS) {
+ throw TrexException("Reached limit of " + std::to_string(MAX_STREAMS) + " streams at the port.");
+ }
get_stateless_obj()->m_rx_flow_stat.add_stream(stream);
m_stream_table.add_stream(stream);
@@ -944,6 +948,141 @@ TrexStatelessPort::remove_and_delete_all_streams() {
}
}
+void
+TrexStatelessPort::start_rx_capture(const std::string &pcap_filename, uint64_t limit) {
+ static MsgReply<bool> reply;
+
+ reply.reset();
+
+ TrexStatelessRxStartCapture *msg = new TrexStatelessRxStartCapture(m_port_id, pcap_filename, limit, reply);
+ send_message_to_rx((TrexStatelessCpToRxMsgBase *)msg);
+
+ /* as below, must wait for ACK from RX core before returning ACK */
+ reply.wait_for_reply();
+}
+
+void
+TrexStatelessPort::stop_rx_capture() {
+ TrexStatelessCpToRxMsgBase *msg = new TrexStatelessRxStopCapture(m_port_id);
+ send_message_to_rx(msg);
+}
+
+void
+TrexStatelessPort::start_rx_queue(uint64_t size) {
+ static MsgReply<bool> reply;
+
+ reply.reset();
+
+ TrexStatelessRxStartQueue *msg = new TrexStatelessRxStartQueue(m_port_id, size, reply);
+ send_message_to_rx( (TrexStatelessCpToRxMsgBase *)msg );
+
+ /* we cannot return ACK to the user until the RX core has approved
+ this might cause the user to lose some packets from the queue
+ */
+ reply.wait_for_reply();
+}
+
+void
+TrexStatelessPort::stop_rx_queue() {
+ TrexStatelessCpToRxMsgBase *msg = new TrexStatelessRxStopQueue(m_port_id);
+ send_message_to_rx(msg);
+}
+
+
+const RXPacketBuffer *
+TrexStatelessPort::get_rx_queue_pkts() {
+ static MsgReply<const RXPacketBuffer *> reply;
+
+ reply.reset();
+
+ TrexStatelessRxQueueGetPkts *msg = new TrexStatelessRxQueueGetPkts(m_port_id, reply);
+ send_message_to_rx( (TrexStatelessCpToRxMsgBase *)msg );
+
+ return reply.wait_for_reply();
+}
+
+
+/**
+ * configures port in L2 mode
+ *
+ */
+void
+TrexStatelessPort::set_l2_mode(const uint8_t *dest_mac) {
+
+ /* not valid under traffic */
+ verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS, "set_l2_mode");
+
+ /* no IPv4 src */
+ getPortAttrObj()->set_src_ipv4(0);
+
+ /* set destination as MAC */
+ getPortAttrObj()->get_dest().set_dest(dest_mac);
+
+ TrexStatelessRxSetL2Mode *msg = new TrexStatelessRxSetL2Mode(m_port_id);
+ send_message_to_rx( (TrexStatelessCpToRxMsgBase *)msg );
+}
+
+/**
+ * configures port in L3 mode - unresolved
+ */
+void
+TrexStatelessPort::set_l3_mode(uint32_t src_ipv4, uint32_t dest_ipv4) {
+
+ /* not valid under traffic */
+ verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS, "set_l3_mode");
+
+ /* set src IPv4 */
+ getPortAttrObj()->set_src_ipv4(src_ipv4);
+
+ /* set dest IPv4 */
+ getPortAttrObj()->get_dest().set_dest(dest_ipv4);
+
+ /* send RX core the relevant info */
+ CManyIPInfo ip_info;
+ ip_info.insert(COneIPv4Info(src_ipv4, 0, getPortAttrObj()->get_src_mac()));
+
+ TrexStatelessRxSetL3Mode *msg = new TrexStatelessRxSetL3Mode(m_port_id, ip_info, false);
+ send_message_to_rx( (TrexStatelessCpToRxMsgBase *)msg );
+}
+
+/**
+ * configures port in L3 mode - resolved
+ *
+ */
+void
+TrexStatelessPort::set_l3_mode(uint32_t src_ipv4, uint32_t dest_ipv4, const uint8_t *resolved_mac) {
+
+ verify_state(PORT_STATE_IDLE | PORT_STATE_STREAMS, "set_l3_mode");
+
+ /* set src IPv4 */
+ getPortAttrObj()->set_src_ipv4(src_ipv4);
+
+ /* set dest IPv4 + resolved MAC */
+ getPortAttrObj()->get_dest().set_dest(dest_ipv4, resolved_mac);
+
+ /* send RX core the relevant info */
+ CManyIPInfo ip_info;
+ ip_info.insert(COneIPv4Info(src_ipv4, 0, getPortAttrObj()->get_src_mac()));
+
+ bool is_grat_arp_needed = !getPortAttrObj()->is_loopback();
+
+ TrexStatelessRxSetL3Mode *msg = new TrexStatelessRxSetL3Mode(m_port_id, ip_info, is_grat_arp_needed);
+ send_message_to_rx( (TrexStatelessCpToRxMsgBase *)msg );
+}
+
+
+Json::Value
+TrexStatelessPort::rx_features_to_json() {
+ static MsgReply<Json::Value> reply;
+
+ reply.reset();
+
+ TrexStatelessRxFeaturesToJson *msg = new TrexStatelessRxFeaturesToJson(m_port_id, reply);
+ send_message_to_rx( (TrexStatelessCpToRxMsgBase *)msg );
+
+ return reply.wait_for_reply();
+}
+
/************* Trex Port Owner **************/
TrexPortOwner::TrexPortOwner() {
diff --git a/src/stateless/cp/trex_stateless_port.h b/src/stateless/cp/trex_stateless_port.h
index e2a2aeba..d4ac4018 100644
--- a/src/stateless/cp/trex_stateless_port.h
+++ b/src/stateless/cp/trex_stateless_port.h
@@ -24,12 +24,15 @@ limitations under the License.
#include "common/basic_utils.h"
#include "internal_api/trex_platform_api.h"
#include "trex_dp_port_events.h"
+#include "trex_stateless_rx_defs.h"
#include "trex_stream.h"
class TrexStatelessCpToDpMsgBase;
class TrexStatelessCpToRxMsgBase;
class TrexStreamsGraphObj;
class TrexPortMultiplier;
+class RXPacketBuffer;
+
/**
* TRex port owner can perform
@@ -218,6 +221,7 @@ public:
*/
void push_remote(const std::string &pcap_filename,
double ipg_usec,
+ double min_ipg_sec,
double speedup,
uint32_t count,
double duration,
@@ -255,11 +259,8 @@ public:
* @author imarom (16-Sep-15)
*
* @param driver
- * @param speed
*/
- void get_properties(std::string &driver, uint32_t &speed);
-
-
+ void get_properties(std::string &driver);
/**
* encode stats as JSON
@@ -362,14 +363,71 @@ public:
double &bps_L2,
double &percentage);
+ void get_pci_info(std::string &pci_addr, int &numa_node);
- void get_macaddr(std::string &hw_macaddr,
- std::string &src_macaddr,
- std::string &dst_macaddr);
- void get_pci_info(std::string &pci_addr, int &numa_node);
+ /**
+ * enable RX capture on port
+ *
+ */
+ void start_rx_capture(const std::string &pcap_filename, uint64_t limit);
+
+ /**
+ * disable RX capture if on
+ *
+ */
+ void stop_rx_capture();
+
+ /**
+ * start RX queueing of packets
+ *
+ * @author imarom (11/7/2016)
+ *
+ * @param limit
+ */
+ void start_rx_queue(uint64_t limit);
+ /**
+ * stop RX queueing
+ *
+ * @author imarom (11/7/2016)
+ */
+ void stop_rx_queue();
+ /**
+ * fetch the RX queue packets from the queue
+ *
+ */
+ const RXPacketBuffer *get_rx_queue_pkts();
+
+ /**
+ * configures port for L2 mode
+ *
+ */
+ void set_l2_mode(const uint8_t *dest_mac);
+
+ /**
+ * configures port in L3 mode
+ *
+ */
+ void set_l3_mode(uint32_t src_ipv4, uint32_t dest_ipv4);
+ void set_l3_mode(uint32_t src_ipv4, uint32_t dest_ipv4, const uint8_t *resolved_mac);
+
+ /**
+ * generate a JSON describing the status
+ * of the RX features
+ *
+ */
+ Json::Value rx_features_to_json();
+
+ /**
+ * return the port attribute object
+ *
+ */
+ TRexPortAttr *getPortAttrObj() {
+ return m_platform_api->getPortAttrObj(m_port_id);
+ }
+
private:
bool is_core_active(int core_id);
@@ -401,7 +459,7 @@ private:
*
*/
void send_message_to_rx(TrexStatelessCpToRxMsgBase *msg);
-
+
/**
* when a port stops, perform various actions
*
@@ -456,6 +514,8 @@ private:
TrexPortOwner m_owner;
int m_pending_async_stop_event;
+ static const uint32_t MAX_STREAMS = 20000;
+
};
@@ -502,9 +562,9 @@ public:
static const std::initializer_list<std::string> g_types;
static const std::initializer_list<std::string> g_ops;
- mul_type_e m_type;
- mul_op_e m_op;
- double m_value;
+ mul_type_e m_type;
+ mul_op_e m_op;
+ double m_value;
};
#endif /* __TREX_STATELESS_PORT_H__ */
diff --git a/src/stateless/dp/trex_stateless_dp_core.cpp b/src/stateless/dp/trex_stateless_dp_core.cpp
index 857ac8f9..6f9376c2 100644
--- a/src/stateless/dp/trex_stateless_dp_core.cpp
+++ b/src/stateless/dp/trex_stateless_dp_core.cpp
@@ -478,6 +478,7 @@ bool TrexStatelessDpPerPort::pause_traffic(uint8_t port_id){
bool TrexStatelessDpPerPort::push_pcap(uint8_t port_id,
const std::string &pcap_filename,
double ipg_usec,
+ double min_ipg_sec,
double speedup,
uint32_t count,
bool is_dual) {
@@ -508,6 +509,7 @@ bool TrexStatelessDpPerPort::push_pcap(uint8_t port_id,
slave_mac_addr,
pcap_filename,
ipg_usec,
+ min_ipg_sec,
speedup,
count,
is_dual);
@@ -1169,6 +1171,7 @@ TrexStatelessDpCore::push_pcap(uint8_t port_id,
int event_id,
const std::string &pcap_filename,
double ipg_usec,
+ double m_min_ipg_sec,
double speedup,
uint32_t count,
double duration,
@@ -1179,7 +1182,7 @@ TrexStatelessDpCore::push_pcap(uint8_t port_id,
lp_port->set_event_id(event_id);
/* delegate the command to the port */
- bool rc = lp_port->push_pcap(port_id, pcap_filename, ipg_usec, speedup, count, is_dual);
+ bool rc = lp_port->push_pcap(port_id, pcap_filename, ipg_usec, m_min_ipg_sec, speedup, count, is_dual);
if (!rc) {
/* report back that we stopped */
CNodeRing *ring = CMsgIns::Ins()->getCpDp()->getRingDpToCp(m_core->m_thread_id);
@@ -1263,6 +1266,7 @@ bool CGenNodePCAP::create(uint8_t port_id,
const uint8_t *slave_mac_addr,
const std::string &pcap_filename,
double ipg_usec,
+ double min_ipg_sec,
double speedup,
uint32_t count,
bool is_dual) {
@@ -1275,13 +1279,14 @@ bool CGenNodePCAP::create(uint8_t port_id,
m_count = count;
m_is_dual = is_dual;
m_dir = dir;
+ m_min_ipg_sec = min_ipg_sec;
/* mark this node as slow path */
set_slow_path(true);
if (ipg_usec != -1) {
/* fixed IPG */
- m_ipg_sec = usec_to_sec(ipg_usec / speedup);
+ m_ipg_sec = std::max(min_ipg_sec, usec_to_sec(ipg_usec / speedup));
m_speedup = 0;
} else {
/* packet IPG */
@@ -1304,9 +1309,7 @@ bool CGenNodePCAP::create(uint8_t port_id,
m_raw_packet = new CCapPktRaw();
if ( m_reader->ReadPacket(m_raw_packet) == false ){
- /* handle error */
- delete m_reader;
- return (false);
+ return false;
}
/* set the dir */
diff --git a/src/stateless/dp/trex_stateless_dp_core.h b/src/stateless/dp/trex_stateless_dp_core.h
index b386daf7..e880a6eb 100644
--- a/src/stateless/dp/trex_stateless_dp_core.h
+++ b/src/stateless/dp/trex_stateless_dp_core.h
@@ -74,6 +74,7 @@ public:
bool push_pcap(uint8_t port_id,
const std::string &pcap_filename,
double ipg_usec,
+ double min_ipg_sec,
double speedup,
uint32_t count,
bool is_dual);
@@ -183,6 +184,7 @@ public:
int event_id,
const std::string &pcap_filename,
double ipg_usec,
+ double min_ipg_sec,
double speedup,
uint32_t count,
double duration,
diff --git a/src/stateless/dp/trex_stream_node.h b/src/stateless/dp/trex_stream_node.h
index dda3113a..bc7be057 100644
--- a/src/stateless/dp/trex_stream_node.h
+++ b/src/stateless/dp/trex_stream_node.h
@@ -468,6 +468,7 @@ public:
const uint8_t *slave_mac_addr,
const std::string &pcap_filename,
double ipg_usec,
+ double min_ipg_sec,
double speedup,
uint32_t count,
bool is_dual);
@@ -536,7 +537,7 @@ public:
if (m_ipg_sec != -1) {
return m_ipg_sec;
} else {
- return ((m_raw_packet->get_time() - m_last_pkt_time) / m_speedup);
+ return (std::max(m_min_ipg_sec, (m_raw_packet->get_time() - m_last_pkt_time) / m_speedup));
}
}
@@ -632,6 +633,7 @@ private:
double m_last_pkt_time;
double m_speedup;
double m_ipg_sec;
+ double m_min_ipg_sec;
uint32_t m_count;
double m_next_time_offset; /* in sec */
@@ -644,7 +646,7 @@ private:
bool m_is_dual;
/* pad to match the size of CGenNode */
- uint8_t m_pad_end[19];
+ uint8_t m_pad_end[11];
} __rte_cache_aligned;
diff --git a/src/stateless/messaging/trex_stateless_messaging.cpp b/src/stateless/messaging/trex_stateless_messaging.cpp
index 95613b41..aeb1e677 100644
--- a/src/stateless/messaging/trex_stateless_messaging.cpp
+++ b/src/stateless/messaging/trex_stateless_messaging.cpp
@@ -191,6 +191,7 @@ TrexStatelessDpPushPCAP::handle(TrexStatelessDpCore *dp_core) {
m_event_id,
m_pcap_filename,
m_ipg_usec,
+ m_min_ipg_sec,
m_speedup,
m_count,
m_duration,
@@ -204,6 +205,7 @@ TrexStatelessDpPushPCAP::clone() {
m_event_id,
m_pcap_filename,
m_ipg_usec,
+ m_min_ipg_sec,
m_speedup,
m_count,
m_duration,
@@ -241,13 +243,15 @@ TrexDpPortEventMsg::handle() {
}
/************************* messages from CP to RX **********************/
-bool TrexStatelessRxStartMsg::handle (CRxCoreStateless *rx_core) {
- rx_core->work();
+bool TrexStatelessRxEnableLatency::handle (CRxCoreStateless *rx_core) {
+ rx_core->enable_latency();
+ m_reply.set_reply(true);
+
return true;
}
-bool TrexStatelessRxStopMsg::handle (CRxCoreStateless *rx_core) {
- rx_core->idle();
+bool TrexStatelessRxDisableLatency::handle (CRxCoreStateless *rx_core) {
+ rx_core->disable_latency();
return true;
}
@@ -255,3 +259,76 @@ bool TrexStatelessRxQuit::handle (CRxCoreStateless *rx_core) {
rx_core->quit();
return true;
}
+
+
+bool
+TrexStatelessRxStartCapture::handle(CRxCoreStateless *rx_core) {
+ rx_core->start_recorder(m_port_id, m_pcap_filename, m_limit);
+
+ /* mark as done */
+ m_reply.set_reply(true);
+
+ return true;
+}
+
+bool
+TrexStatelessRxStopCapture::handle(CRxCoreStateless *rx_core) {
+ rx_core->stop_recorder(m_port_id);
+
+ return true;
+}
+
+bool
+TrexStatelessRxStartQueue::handle(CRxCoreStateless *rx_core) {
+ rx_core->start_queue(m_port_id, m_size);
+
+ /* mark as done */
+ m_reply.set_reply(true);
+
+ return true;
+}
+
+bool
+TrexStatelessRxStopQueue::handle(CRxCoreStateless *rx_core) {
+ rx_core->stop_queue(m_port_id);
+
+ return true;
+}
+
+
+
+bool
+TrexStatelessRxQueueGetPkts::handle(CRxCoreStateless *rx_core) {
+ const RXPacketBuffer *pkt_buffer = rx_core->get_rx_queue_pkts(m_port_id);
+
+ /* set the reply */
+ m_reply.set_reply(pkt_buffer);
+
+ return true;
+}
+
+
+bool
+TrexStatelessRxFeaturesToJson::handle(CRxCoreStateless *rx_core) {
+ Json::Value output = rx_core->get_rx_port_mngr(m_port_id).to_json();
+
+ /* set the reply */
+ m_reply.set_reply(output);
+
+ return true;
+}
+
+bool
+TrexStatelessRxSetL2Mode::handle(CRxCoreStateless *rx_core) {
+ rx_core->get_rx_port_mngr(m_port_id).set_l2_mode();
+
+ return true;
+}
+
+bool
+TrexStatelessRxSetL3Mode::handle(CRxCoreStateless *rx_core) {
+ rx_core->get_rx_port_mngr(m_port_id).set_l3_mode(m_src_addr, m_is_grat_arp_needed);
+
+ return true;
+}
+
diff --git a/src/stateless/messaging/trex_stateless_messaging.h b/src/stateless/messaging/trex_stateless_messaging.h
index fb2c27ab..72b92d11 100644
--- a/src/stateless/messaging/trex_stateless_messaging.h
+++ b/src/stateless/messaging/trex_stateless_messaging.h
@@ -24,11 +24,67 @@ limitations under the License.
#include "msg_manager.h"
#include "trex_dp_port_events.h"
+#include "trex_exception.h"
+#include "trex_stateless_rx_defs.h"
+#include "os_time.h"
+#include "utl_ip.h"
class TrexStatelessDpCore;
class CRxCoreStateless;
class TrexStreamsCompiledObj;
class CFlowGenListPerThread;
+class RXPacketBuffer;
+
+/**
+ * Generic message reply object
+ *
+ * @author imarom (11/27/2016)
+ */
+template<typename T> class MsgReply {
+
+public:
+
+ MsgReply() {
+ reset();
+ }
+
+ void reset() {
+ m_pending = true;
+ }
+
+ bool is_pending() const {
+ return m_pending;
+ }
+
+ void set_reply(const T &reply) {
+ m_reply = reply;
+
+ /* before marking as done make sure all stores are committed */
+ asm volatile("mfence" ::: "memory");
+ m_pending = false;
+ }
+
+ T wait_for_reply(int timeout_ms = 500, int backoff_ms = 1) {
+ int guard = timeout_ms;
+
+ while (is_pending()) {
+ guard -= backoff_ms;
+ if (guard < 0) {
+ throw TrexException("timeout: failed to get reply from core");
+ }
+
+ delay(backoff_ms);
+ }
+
+ return m_reply;
+
+ }
+
+protected:
+ volatile bool m_pending;
+ T m_reply;
+};
+
/**
* defines the base class for CP to DP messages
@@ -257,6 +313,7 @@ public:
int event_id,
const std::string &pcap_filename,
double ipg_usec,
+ double min_ipg_sec,
double speedup,
uint32_t count,
double duration,
@@ -265,6 +322,7 @@ public:
m_port_id = port_id;
m_event_id = event_id;
m_ipg_usec = ipg_usec;
+ m_min_ipg_sec = min_ipg_sec;
m_speedup = speedup;
m_count = count;
m_duration = duration;
@@ -279,6 +337,7 @@ private:
std::string m_pcap_filename;
int m_event_id;
double m_ipg_usec;
+ double m_min_ipg_sec;
double m_speedup;
double m_duration;
uint32_t m_count;
@@ -312,7 +371,7 @@ private:
/************************* messages from DP to CP **********************/
/**
- * defines the base class for CP to DP messages
+ * defines the base class for DP to CP messages
*
* @author imarom (27-Oct-15)
*/
@@ -404,11 +463,19 @@ public:
};
-class TrexStatelessRxStartMsg : public TrexStatelessCpToRxMsgBase {
+
+class TrexStatelessRxEnableLatency : public TrexStatelessCpToRxMsgBase {
+public:
+ TrexStatelessRxEnableLatency(MsgReply<bool> &reply) : m_reply(reply) {
+ }
+
bool handle (CRxCoreStateless *rx_core);
+
+private:
+ MsgReply<bool> &m_reply;
};
-class TrexStatelessRxStopMsg : public TrexStatelessCpToRxMsgBase {
+class TrexStatelessRxDisableLatency : public TrexStatelessCpToRxMsgBase {
bool handle (CRxCoreStateless *rx_core);
};
@@ -416,4 +483,151 @@ class TrexStatelessRxQuit : public TrexStatelessCpToRxMsgBase {
bool handle (CRxCoreStateless *rx_core);
};
+
+
+class TrexStatelessRxStartCapture : public TrexStatelessCpToRxMsgBase {
+public:
+ TrexStatelessRxStartCapture(uint8_t port_id,
+ const std::string &pcap_filename,
+ uint64_t limit,
+ MsgReply<bool> &reply) : m_reply(reply) {
+
+ m_port_id = port_id;
+ m_limit = limit;
+ m_pcap_filename = pcap_filename;
+ }
+
+ virtual bool handle(CRxCoreStateless *rx_core);
+
+private:
+ uint8_t m_port_id;
+ std::string m_pcap_filename;
+ uint64_t m_limit;
+ MsgReply<bool> &m_reply;
+};
+
+
+class TrexStatelessRxStopCapture : public TrexStatelessCpToRxMsgBase {
+public:
+ TrexStatelessRxStopCapture(uint8_t port_id) {
+ m_port_id = port_id;
+ }
+
+ virtual bool handle(CRxCoreStateless *rx_core);
+
+private:
+ uint8_t m_port_id;
+};
+
+
+class TrexStatelessRxStartQueue : public TrexStatelessCpToRxMsgBase {
+public:
+ TrexStatelessRxStartQueue(uint8_t port_id,
+ uint64_t size,
+ MsgReply<bool> &reply) : m_reply(reply) {
+
+ m_port_id = port_id;
+ m_size = size;
+ }
+
+ virtual bool handle(CRxCoreStateless *rx_core);
+
+private:
+ uint8_t m_port_id;
+ uint64_t m_size;
+ MsgReply<bool> &m_reply;
+};
+
+
+class TrexStatelessRxStopQueue : public TrexStatelessCpToRxMsgBase {
+public:
+ TrexStatelessRxStopQueue(uint8_t port_id) {
+ m_port_id = port_id;
+ }
+
+ virtual bool handle(CRxCoreStateless *rx_core);
+
+private:
+ uint8_t m_port_id;
+};
+
+
+
+class TrexStatelessRxQueueGetPkts : public TrexStatelessCpToRxMsgBase {
+public:
+
+ TrexStatelessRxQueueGetPkts(uint8_t port_id, MsgReply<const RXPacketBuffer *> &reply) : m_reply(reply) {
+ m_port_id = port_id;
+ }
+
+ /**
+ * virtual function to handle a message
+ *
+ */
+ virtual bool handle(CRxCoreStateless *rx_core);
+
+private:
+ uint8_t m_port_id;
+ MsgReply<const RXPacketBuffer *> &m_reply;
+
+};
+
+/**
+ * updates the RX core that we are in L2 mode
+ */
+class TrexStatelessRxSetL2Mode : public TrexStatelessCpToRxMsgBase {
+public:
+ TrexStatelessRxSetL2Mode(uint8_t port_id) {
+ m_port_id = port_id;
+ }
+
+ virtual bool handle(CRxCoreStateless *rx_core);
+
+private:
+ uint8_t m_port_id;
+};
+
+/**
+ * updates the RX core that we are in a L3 mode
+ */
+class TrexStatelessRxSetL3Mode : public TrexStatelessCpToRxMsgBase {
+public:
+ TrexStatelessRxSetL3Mode(uint8_t port_id,
+ const CManyIPInfo &src_addr,
+ bool is_grat_arp_needed) {
+
+ m_port_id = port_id;
+ m_src_addr = src_addr;
+ m_is_grat_arp_needed = is_grat_arp_needed;
+ }
+
+ virtual bool handle(CRxCoreStateless *rx_core);
+
+private:
+ uint8_t m_port_id;
+ CManyIPInfo m_src_addr;
+ bool m_is_grat_arp_needed;
+};
+
+/**
+ * a request from RX core to dump to Json the RX features
+ */
+class TrexStatelessRxFeaturesToJson : public TrexStatelessCpToRxMsgBase {
+public:
+
+ TrexStatelessRxFeaturesToJson(uint8_t port_id, MsgReply<Json::Value> &reply) : m_reply(reply) {
+ m_port_id = port_id;
+ }
+
+ /**
+ * virtual function to handle a message
+ *
+ */
+ virtual bool handle(CRxCoreStateless *rx_core);
+
+private:
+ uint8_t m_port_id;
+ MsgReply<Json::Value> &m_reply;
+};
+
#endif /* __TREX_STATELESS_MESSAGING_H__ */
diff --git a/src/stateless/rx/trex_stateless_rx_core.cpp b/src/stateless/rx/trex_stateless_rx_core.cpp
index d162c5b3..d27485de 100644
--- a/src/stateless/rx/trex_stateless_rx_core.cpp
+++ b/src/stateless/rx/trex_stateless_rx_core.cpp
@@ -26,6 +26,8 @@
#include "pal/linux/sanb_atomic.h"
#include "trex_stateless_messaging.h"
#include "trex_stateless_rx_core.h"
+#include "trex_stateless.h"
+
void CRFC2544Info::create() {
m_latency.Create();
@@ -64,15 +66,7 @@ void CRFC2544Info::export_data(rfc2544_info_t_ &obj) {
obj.set_latency_json(json);
};
-void CCPortLatencyStl::reset() {
- for (int i = 0; i < MAX_FLOW_STATS; i++) {
- m_rx_pg_stat[i].clear();
- m_rx_pg_stat_payload[i].clear();
- }
-}
-
void CRxCoreStateless::create(const CRxSlCfg &cfg) {
- m_rcv_all = false;
m_capture = false;
m_max_ports = cfg.m_max_ports;
@@ -82,15 +76,21 @@ void CRxCoreStateless::create(const CRxSlCfg &cfg) {
m_ring_to_cp = cp_rx->getRingDpToCp(0);
m_state = STATE_IDLE;
- for (int i = 0; i < m_max_ports; i++) {
- CLatencyManagerPerPortStl * lp = &m_ports[i];
- lp->m_io = cfg.m_ports[i];
- lp->m_port.reset();
+ for (int i = 0; i < MAX_FLOW_STATS_PAYLOAD; i++) {
+ m_rfc2544[i].create();
}
+
m_cpu_cp_u.Create(&m_cpu_dp_u);
- for (int i = 0; i < MAX_FLOW_STATS_PAYLOAD; i++) {
- m_rfc2544[i].create();
+ /* create per port manager */
+ for (int i = 0; i < m_max_ports; i++) {
+ const TRexPortAttr *port_attr = get_stateless_obj()->get_platform_api()->getPortAttrObj(i);
+ m_rx_port_mngr[i].create(port_attr,
+ cfg.m_ports[i],
+ m_rfc2544,
+ &m_err_cntrs,
+ &m_cpu_dp_u,
+ cfg.m_num_crc_fix_bytes);
}
}
@@ -124,10 +124,31 @@ bool CRxCoreStateless::periodic_check_for_cp_messages() {
handle_cp_msg(msg);
}
+ /* a message might result in a change of state */
+ recalculate_next_state();
return true;
}
+void CRxCoreStateless::recalculate_next_state() {
+ if (m_state == STATE_QUIT) {
+ return;
+ }
+
+ /* next state is determine by the question are there any ports with active features ? */
+ m_state = (are_any_features_active() ? STATE_WORKING : STATE_IDLE);
+}
+
+bool CRxCoreStateless::are_any_features_active() {
+ for (int i = 0; i < m_max_ports; i++) {
+ if (m_rx_port_mngr[i].has_features_set()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void CRxCoreStateless::idle_state_loop() {
const int SHORT_DELAY_MS = 2;
const int LONG_DELAY_MS = 50;
@@ -141,7 +162,7 @@ void CRxCoreStateless::idle_state_loop() {
counter = 0;
continue;
} else {
- flush_rx();
+ flush_all_pending_pkts();
}
/* enter deep sleep only if enough time had passed */
@@ -154,284 +175,113 @@ void CRxCoreStateless::idle_state_loop() {
}
}
-void CRxCoreStateless::start() {
- int count = 0;
- int i = 0;
- bool do_try_rx_queue =CGlobalInfo::m_options.preview.get_vm_one_queue_enable() ? true : false;
-
- /* register a watchdog handle on current core */
- m_monitor.create("STL RX CORE", 1);
- TrexWatchDog::getInstance().register_monitor(&m_monitor);
-
- while (true) {
- if (m_state == STATE_WORKING) {
- i++;
- if (i == 100000) { // approx 10msec
- i = 0;
- periodic_check_for_cp_messages(); // m_state might change in here
- }
- } else {
- if (m_state == STATE_QUIT)
- break;
- count = 0;
- i = 0;
- set_working_msg_ack(false);
- idle_state_loop();
- set_working_msg_ack(true);
- }
- if (do_try_rx_queue) {
- try_rx_queues();
- }
- count += try_rx();
+/**
+ * for each port give a tick (for flushing if needed)
+ *
+ */
+void CRxCoreStateless::port_manager_tick() {
+ for (int i = 0; i < m_max_ports; i++) {
+ m_rx_port_mngr[i].tick();
}
- rte_pause();
-
- m_monitor.disable();
}
-void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t *m) {
- CFlowStatParser parser;
-
- if (m_rcv_all || parser.parse(rte_pktmbuf_mtod(m, uint8_t *), m->pkt_len) == 0) {
- uint32_t ip_id;
- if (m_rcv_all || (parser.get_ip_id(ip_id) == 0)) {
- if (m_rcv_all || is_flow_stat_id(ip_id)) {
- uint16_t hw_id;
- if (m_rcv_all || is_flow_stat_payload_id(ip_id)) {
- bool good_packet = true;
- uint8_t *p = rte_pktmbuf_mtod(m, uint8_t*);
- struct flow_stat_payload_header *fsp_head = (struct flow_stat_payload_header *)
- (p + m->pkt_len - sizeof(struct flow_stat_payload_header));
- hw_id = fsp_head->hw_id;
- CRFC2544Info *curr_rfc2544;
-
- if (unlikely(fsp_head->magic != FLOW_STAT_PAYLOAD_MAGIC) || hw_id >= MAX_FLOW_STATS_PAYLOAD) {
- good_packet = false;
- if (!m_rcv_all)
- m_err_cntrs.m_bad_header++;
- } else {
- curr_rfc2544 = &m_rfc2544[hw_id];
-
- if (fsp_head->flow_seq != curr_rfc2544->get_exp_flow_seq()) {
- // bad flow seq num
- // Might be the first packet of a new flow, packet from an old flow, or garbage.
-
- if (fsp_head->flow_seq == curr_rfc2544->get_prev_flow_seq()) {
- // packet from previous flow using this hw_id that arrived late
- good_packet = false;
- m_err_cntrs.m_old_flow++;
- } else {
- if (curr_rfc2544->no_flow_seq()) {
- // first packet we see from this flow
- good_packet = true;
- curr_rfc2544->set_exp_flow_seq(fsp_head->flow_seq);
- } else {
- // garbage packet
- good_packet = false;
- m_err_cntrs.m_bad_header++;
- }
- }
- }
- }
-
- if (good_packet) {
- uint32_t pkt_seq = fsp_head->seq;
- uint32_t exp_seq = curr_rfc2544->get_seq();
- if (unlikely(pkt_seq != exp_seq)) {
- if (pkt_seq < exp_seq) {
- if (exp_seq - pkt_seq > 100000) {
- // packet loss while we had wrap around
- curr_rfc2544->inc_seq_err(pkt_seq - exp_seq);
- curr_rfc2544->inc_seq_err_too_big();
- curr_rfc2544->set_seq(pkt_seq + 1);
- } else {
- if (pkt_seq == (exp_seq - 1)) {
- curr_rfc2544->inc_dup();
- } else {
- curr_rfc2544->inc_ooo();
- // We thought it was lost, but it was just out of order
- curr_rfc2544->dec_seq_err();
- }
- curr_rfc2544->inc_seq_err_too_low();
- }
- } else {
- if (unlikely (pkt_seq - exp_seq > 100000)) {
- // packet reorder while we had wrap around
- if (pkt_seq == (exp_seq - 1)) {
- curr_rfc2544->inc_dup();
- } else {
- curr_rfc2544->inc_ooo();
- // We thought it was lost, but it was just out of order
- curr_rfc2544->dec_seq_err();
- }
- curr_rfc2544->inc_seq_err_too_low();
- } else {
- // seq > curr_rfc2544->seq. Assuming lost packets
- curr_rfc2544->inc_seq_err(pkt_seq - exp_seq);
- curr_rfc2544->inc_seq_err_too_big();
- curr_rfc2544->set_seq(pkt_seq + 1);
- }
- }
- } else {
- curr_rfc2544->set_seq(pkt_seq + 1);
- }
- lp->m_port.m_rx_pg_stat_payload[hw_id].add_pkts(1);
- lp->m_port.m_rx_pg_stat_payload[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC
- uint64_t d = (os_get_hr_tick_64() - fsp_head->time_stamp );
- dsec_t ctime = ptime_convert_hr_dsec(d);
- curr_rfc2544->add_sample(ctime);
- }
- } else {
- hw_id = get_hw_id(ip_id);
- if (hw_id < MAX_FLOW_STATS) {
- lp->m_port.m_rx_pg_stat[hw_id].add_pkts(1);
- lp->m_port.m_rx_pg_stat[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC
- }
- }
- }
- }
+/**
+ * for each port handle the grat ARP mechansim
+ *
+ */
+void CRxCoreStateless::handle_grat_arp() {
+ for (int i = 0; i < m_max_ports; i++) {
+ m_rx_port_mngr[i].send_next_grat_arp();
}
}
-void CRxCoreStateless::capture_pkt(rte_mbuf_t *m) {
+void CRxCoreStateless::handle_work_stage() {
+
+ /* set the next sync time to */
+ dsec_t sync_time_sec = now_sec() + (1.0 / 1000);
+ dsec_t tick_time_sec = now_sec() + 1.0;
+ dsec_t grat_arp_sec = now_sec() + (double)CGlobalInfo::m_options.m_arp_ref_per;
-}
+ while (m_state == STATE_WORKING) {
+ process_all_pending_pkts();
-// In VM setup, handle packets coming as messages from DP cores.
-void CRxCoreStateless::handle_rx_queue_msgs(uint8_t thread_id, CNodeRing * r) {
- while ( true ) {
- CGenNode * node;
- if ( r->Dequeue(node) != 0 ) {
- break;
+ dsec_t now = now_sec();
+
+ /* until a scheduler is added here - dirty IFs */
+
+ if ( (now - sync_time_sec) > 0 ) {
+ periodic_check_for_cp_messages();
+ sync_time_sec = now + (1.0 / 1000);
+ }
+
+ if ( (now - tick_time_sec) > 0) {
+ port_manager_tick();
+ tick_time_sec = now + 1.0;
}
- assert(node);
- CGenNodeMsgBase * msg = (CGenNodeMsgBase *)node;
- CGenNodeLatencyPktInfo * l_msg;
- uint8_t msg_type = msg->m_msg_type;
- uint8_t rx_port_index;
- CLatencyManagerPerPortStl * lp;
-
- switch (msg_type) {
- case CGenNodeMsgBase::LATENCY_PKT:
- l_msg = (CGenNodeLatencyPktInfo *)msg;
- assert(l_msg->m_latency_offset == 0xdead);
- rx_port_index = (thread_id << 1) + (l_msg->m_dir & 1);
- assert( rx_port_index < m_max_ports );
- lp = &m_ports[rx_port_index];
- handle_rx_pkt(lp, (rte_mbuf_t *)l_msg->m_pkt);
- if (m_capture)
- capture_pkt((rte_mbuf_t *)l_msg->m_pkt);
- rte_pktmbuf_free((rte_mbuf_t *)l_msg->m_pkt);
- break;
- default:
- printf("ERROR latency-thread message type is not valid %d \n", msg_type);
- assert(0);
+ if ( (now - grat_arp_sec) > 0) {
+ handle_grat_arp();
+ grat_arp_sec = now + (double)CGlobalInfo::m_options.m_arp_ref_per;
}
- CGlobalInfo::free_node(node);
+ rte_pause();
+
}
}
-// VM mode function. Handle messages from DP
-void CRxCoreStateless::try_rx_queues() {
+void CRxCoreStateless::start() {
+ /* register a watchdog handle on current core */
+ m_monitor.create("STL RX CORE", 1);
+ TrexWatchDog::getInstance().register_monitor(&m_monitor);
- CMessagingManager * rx_dp = CMsgIns::Ins()->getRxDp();
- uint8_t threads=CMsgIns::Ins()->get_num_threads();
- int ti;
- for (ti = 0; ti < (int)threads; ti++) {
- CNodeRing * r = rx_dp->getRingDpToCp(ti);
- if ( ! r->isEmpty() ) {
- handle_rx_queue_msgs((uint8_t)ti, r);
+ recalculate_next_state();
+
+ while (m_state != STATE_QUIT) {
+ switch (m_state) {
+ case STATE_IDLE:
+ idle_state_loop();
+ break;
+ case STATE_WORKING:
+ handle_work_stage();
+ break;
+
+ default:
+ assert(0);
+ break;
}
+
}
-}
-// exactly the same as try_rx, without the handle_rx_pkt
-// purpose is to flush rx queues when core is in idle state
-void CRxCoreStateless::flush_rx() {
- rte_mbuf_t * rx_pkts[64];
- int i, total_pkts = 0;
- for (i = 0; i < m_max_ports; i++) {
- CLatencyManagerPerPortStl * lp = &m_ports[i];
- rte_mbuf_t * m;
- /* try to read 64 packets clean up the queue */
- uint16_t cnt_p = lp->m_io->rx_burst(rx_pkts, 64);
- total_pkts += cnt_p;
- if (cnt_p) {
- m_cpu_dp_u.start_work1();
- int j;
- for (j = 0; j < cnt_p; j++) {
- m = rx_pkts[j];
- rte_pktmbuf_free(m);
- }
- /* commit only if there was work to do ! */
- m_cpu_dp_u.commit1();
- }/* if work */
- }// all ports
+ m_monitor.disable();
}
-int CRxCoreStateless::try_rx() {
- rte_mbuf_t * rx_pkts[64];
- int i, total_pkts = 0;
- for (i = 0; i < m_max_ports; i++) {
- CLatencyManagerPerPortStl * lp = &m_ports[i];
- rte_mbuf_t * m;
- /* try to read 64 packets clean up the queue */
- uint16_t cnt_p = lp->m_io->rx_burst(rx_pkts, 64);
- total_pkts += cnt_p;
- if (cnt_p) {
- m_cpu_dp_u.start_work1();
- int j;
- for (j = 0; j < cnt_p; j++) {
- m = rx_pkts[j];
- handle_rx_pkt(lp, m);
- rte_pktmbuf_free(m);
- }
- /* commit only if there was work to do ! */
- m_cpu_dp_u.commit1();
- }/* if work */
- }// all ports
- return total_pkts;
-}
+void CRxCoreStateless::capture_pkt(rte_mbuf_t *m) {
-bool CRxCoreStateless::is_flow_stat_id(uint32_t id) {
- if ((id & 0x000fff00) == IP_ID_RESERVE_BASE) return true;
- return false;
}
-bool CRxCoreStateless::is_flow_stat_payload_id(uint32_t id) {
- if (id == FLOW_STAT_PAYLOAD_IP_ID) return true;
- return false;
-}
+int CRxCoreStateless::process_all_pending_pkts(bool flush_rx) {
+
+ int total_pkts = 0;
+ for (int i = 0; i < m_max_ports; i++) {
+ total_pkts += m_rx_port_mngr[i].process_all_pending_pkts(flush_rx);
+ }
+
+ return total_pkts;
-uint16_t CRxCoreStateless::get_hw_id(uint16_t id) {
- return (0x00ff & id);
}
void CRxCoreStateless::reset_rx_stats(uint8_t port_id) {
- for (int hw_id = 0; hw_id < MAX_FLOW_STATS; hw_id++) {
- m_ports[port_id].m_port.m_rx_pg_stat[hw_id].clear();
- }
+ m_rx_port_mngr[port_id].clear_stats();
}
int CRxCoreStateless::get_rx_stats(uint8_t port_id, rx_per_flow_t *rx_stats, int min, int max
, bool reset, TrexPlatformApi::driver_stat_cap_e type) {
- for (int hw_id = min; hw_id <= max; hw_id++) {
- if (type == TrexPlatformApi::IF_STAT_PAYLOAD) {
- rx_stats[hw_id - min] = m_ports[port_id].m_port.m_rx_pg_stat_payload[hw_id];
- } else {
- rx_stats[hw_id - min] = m_ports[port_id].m_port.m_rx_pg_stat[hw_id];
- }
- if (reset) {
- if (type == TrexPlatformApi::IF_STAT_PAYLOAD) {
- m_ports[port_id].m_port.m_rx_pg_stat_payload[hw_id].clear();
- } else {
- m_ports[port_id].m_port.m_rx_pg_stat[hw_id].clear();
- }
- }
- }
- return 0;
+
+ /* for now only latency stats */
+ m_rx_port_mngr[port_id].get_latency_stats(rx_stats, min, max, reset, type);
+
+ return (0);
+
}
int CRxCoreStateless::get_rfc2544_info(rfc2544_info_t *rfc2544_info, int min, int max, bool reset) {
@@ -458,13 +308,6 @@ int CRxCoreStateless::get_rx_err_cntrs(CRxCoreErrCntrs *rx_err) {
return 0;
}
-void CRxCoreStateless::set_working_msg_ack(bool val) {
- sanb_smp_memory_barrier();
- m_ack_start_work_msg = val;
- sanb_smp_memory_barrier();
-}
-
-
void CRxCoreStateless::update_cpu_util(){
m_cpu_cp_u.Update();
}
@@ -472,3 +315,58 @@ void CRxCoreStateless::update_cpu_util(){
double CRxCoreStateless::get_cpu_util() {
return m_cpu_cp_u.GetVal();
}
+
+
+void
+CRxCoreStateless::start_recorder(uint8_t port_id, const std::string &pcap_filename, uint64_t limit) {
+ m_rx_port_mngr[port_id].start_recorder(pcap_filename, limit);
+ recalculate_next_state();
+}
+
+void
+CRxCoreStateless::stop_recorder(uint8_t port_id) {
+ m_rx_port_mngr[port_id].stop_recorder();
+ recalculate_next_state();
+}
+
+void
+CRxCoreStateless::start_queue(uint8_t port_id, uint64_t size) {
+ m_rx_port_mngr[port_id].start_queue(size);
+ recalculate_next_state();
+}
+
+void
+CRxCoreStateless::stop_queue(uint8_t port_id) {
+ m_rx_port_mngr[port_id].stop_queue();
+ recalculate_next_state();
+}
+
+void
+CRxCoreStateless::enable_latency() {
+ for (int i = 0; i < m_max_ports; i++) {
+ m_rx_port_mngr[i].enable_latency();
+ }
+
+ recalculate_next_state();
+}
+
+void
+CRxCoreStateless::disable_latency() {
+ for (int i = 0; i < m_max_ports; i++) {
+ m_rx_port_mngr[i].disable_latency();
+ }
+
+ recalculate_next_state();
+}
+
+RXPortManager &
+CRxCoreStateless::get_rx_port_mngr(uint8_t port_id) {
+ assert(port_id < m_max_ports);
+ return m_rx_port_mngr[port_id];
+
+}
+
+void
+CRxCoreStateless::get_ignore_stats(int port_id, CRXCoreIgnoreStat &stat, bool get_diff) {
+ get_rx_port_mngr(port_id).get_ignore_stats(stat, get_diff);
+}
diff --git a/src/stateless/rx/trex_stateless_rx_core.h b/src/stateless/rx/trex_stateless_rx_core.h
index 3f9fb6cc..4eed59a1 100644
--- a/src/stateless/rx/trex_stateless_rx_core.h
+++ b/src/stateless/rx/trex_stateless_rx_core.h
@@ -21,10 +21,12 @@
#ifndef __TREX_STATELESS_RX_CORE_H__
#define __TREX_STATELESS_RX_CORE_H__
#include <stdint.h>
+
#include "stateful_rx_core.h"
#include "os_time.h"
#include "pal/linux/sanb_atomic.h"
#include "utl_cpuu.h"
+#include "trex_stateless_rx_port_mngr.h"
class TrexStatelessCpToRxMsgBase;
@@ -37,25 +39,6 @@ class CCPortLatencyStl {
rx_per_flow_t m_rx_pg_stat_payload[MAX_FLOW_STATS_PAYLOAD];
};
-class CLatencyManagerPerPortStl {
-public:
- CCPortLatencyStl m_port;
- CPortLatencyHWBase * m_io;
-};
-
-class CRxSlCfg {
- public:
- CRxSlCfg (){
- m_max_ports = 0;
- m_cps = 0.0;
- }
-
- public:
- uint32_t m_max_ports;
- double m_cps;
- CPortLatencyHWBase * m_ports[TREX_MAX_PORTS];
-};
-
class CRFC2544Info {
public:
void create();
@@ -109,12 +92,20 @@ class CRxCoreErrCntrs {
m_old_flow = 0;
}
- private:
+ public:
uint64_t m_bad_header;
uint64_t m_old_flow;
};
+/**
+ * stateless RX core
+ *
+ */
class CRxCoreStateless {
+
+ /**
+ * core states
+ */
enum state_e {
STATE_IDLE,
STATE_WORKING,
@@ -129,47 +120,85 @@ class CRxCoreStateless {
, TrexPlatformApi::driver_stat_cap_e type);
int get_rfc2544_info(rfc2544_info_t *rfc2544_info, int min, int max, bool reset);
int get_rx_err_cntrs(CRxCoreErrCntrs *rx_err);
- void work() {
- m_state = STATE_WORKING;
- m_err_cntrs.reset(); // When starting to work, reset global counters
- }
- void idle() {m_state = STATE_IDLE;}
+
+
void quit() {m_state = STATE_QUIT;}
- bool is_working() const {return (m_ack_start_work_msg == true);}
- void set_working_msg_ack(bool val);
+ bool is_working() const {return (m_state == STATE_WORKING);}
double get_cpu_util();
void update_cpu_util();
+ const RXPacketBuffer *get_rx_queue_pkts(uint8_t port_id) {
+ return m_rx_port_mngr[port_id].get_pkt_buffer();
+ }
+
+ /**
+ * start capturing of RX packets on a specific port
+ *
+ */
+ void start_recorder(uint8_t port_id, const std::string &pcap_filename, uint64_t limit);
+ void stop_recorder(uint8_t port_id);
+
+ /**
+ * start RX queueing of packets
+ *
+ */
+ void start_queue(uint8_t port_id, uint64_t size);
+ void stop_queue(uint8_t port_id);
+
+ /**
+ * enable latency feature for RX packets
+ * will be apply to all ports
+ */
+ void enable_latency();
+ void disable_latency();
+
+ RXPortManager &get_rx_port_mngr(uint8_t port_id);
+
+ /**
+ * fetch the ignored stats for a port
+ *
+ */
+ void get_ignore_stats(int port_id, CRXCoreIgnoreStat &stat, bool get_diff);
private:
void handle_cp_msg(TrexStatelessCpToRxMsgBase *msg);
bool periodic_check_for_cp_messages();
void tickle();
void idle_state_loop();
- void handle_rx_pkt(CLatencyManagerPerPortStl * lp, rte_mbuf_t * m);
+
+ void recalculate_next_state();
+ bool are_any_features_active();
+
void capture_pkt(rte_mbuf_t *m);
void handle_rx_queue_msgs(uint8_t thread_id, CNodeRing * r);
- void flush_rx();
- int try_rx();
- void try_rx_queues();
- bool is_flow_stat_id(uint32_t id);
- bool is_flow_stat_payload_id(uint32_t id);
- uint16_t get_hw_id(uint16_t id);
+ void handle_work_stage();
+ void port_manager_tick();
+ void handle_grat_arp();
+
+ int process_all_pending_pkts(bool flush_rx = false);
+ void flush_all_pending_pkts() {
+ process_all_pending_pkts(true);
+ }
+
+ void try_rx_queues();
+
private:
- TrexMonitor m_monitor;
- uint32_t m_max_ports;
- bool m_capture;
- bool m_rcv_all;
- CLatencyManagerPerPortStl m_ports[TREX_MAX_PORTS];
- state_e m_state;
- CNodeRing *m_ring_from_cp;
- CNodeRing *m_ring_to_cp;
- CCpuUtlDp m_cpu_dp_u;
- CCpuUtlCp m_cpu_cp_u;
+ TrexMonitor m_monitor;
+ uint32_t m_max_ports;
+ bool m_capture;
+ state_e m_state;
+ CNodeRing *m_ring_from_cp;
+ CNodeRing *m_ring_to_cp;
+ CCpuUtlDp m_cpu_dp_u;
+ CCpuUtlCp m_cpu_cp_u;
+
// Used for acking "work" (go out of idle) messages from cp
volatile bool m_ack_start_work_msg __rte_cache_aligned;
+
CRxCoreErrCntrs m_err_cntrs;
CRFC2544Info m_rfc2544[MAX_FLOW_STATS_PAYLOAD];
+
+ RXPortManager m_rx_port_mngr[TREX_MAX_PORTS];
};
#endif
diff --git a/src/stateless/rx/trex_stateless_rx_defs.h b/src/stateless/rx/trex_stateless_rx_defs.h
new file mode 100644
index 00000000..aefcc133
--- /dev/null
+++ b/src/stateless/rx/trex_stateless_rx_defs.h
@@ -0,0 +1,62 @@
+/*
+ Itay Marom
+ Cisco Systems, Inc.
+*/
+
+/*
+ Copyright (c) 2016-2016 Cisco Systems, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef __TREX_STATELESS_RX_DEFS_H__
+#define __TREX_STATELESS_RX_DEFS_H__
+
+#include "trex_defs.h"
+#include <json/json.h>
+
+class CPortLatencyHWBase;
+
+
+/**
+ * general SL cfg
+ *
+ */
+class CRxSlCfg {
+ public:
+ CRxSlCfg (){
+ m_max_ports = 0;
+ m_cps = 0.0;
+ m_num_crc_fix_bytes = 0;
+ }
+
+ public:
+ uint32_t m_max_ports;
+ double m_cps;
+ CPortLatencyHWBase * m_ports[TREX_MAX_PORTS];
+ uint8_t m_num_crc_fix_bytes;
+};
+
+/**
+ * describes the filter type applied to the RX
+ * RX_FILTER_MODE_HW - only hardware filtered traffic will
+ * reach the RX core
+ *
+ */
+typedef enum rx_filter_mode_ {
+ RX_FILTER_MODE_HW,
+ RX_FILTER_MODE_ALL
+} rx_filter_mode_e;
+
+#endif /* __TREX_STATELESS_RX_DEFS_H__ */
+
diff --git a/src/stateless/rx/trex_stateless_rx_port_mngr.cpp b/src/stateless/rx/trex_stateless_rx_port_mngr.cpp
new file mode 100644
index 00000000..5aac1ea6
--- /dev/null
+++ b/src/stateless/rx/trex_stateless_rx_port_mngr.cpp
@@ -0,0 +1,889 @@
+/*
+ Itay Marom
+ Cisco Systems, Inc.
+*/
+
+/*
+ Copyright (c) 2016-2016 Cisco Systems, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+#include "bp_sim.h"
+#include "trex_stateless_rx_port_mngr.h"
+#include "common/captureFile.h"
+#include "trex_stateless_rx_core.h"
+#include "common/Network/Packet/Arp.h"
+#include "pkt_gen.h"
+
+/**************************************
+ * latency RX feature
+ *
+ *************************************/
+RXLatency::RXLatency() {
+ m_rcv_all = false;
+ m_rfc2544 = NULL;
+ m_err_cntrs = NULL;
+
+ for (int i = 0; i < MAX_FLOW_STATS; i++) {
+ m_rx_pg_stat[i].clear();
+ m_rx_pg_stat_payload[i].clear();
+ }
+}
+
+void
+RXLatency::create(CRFC2544Info *rfc2544, CRxCoreErrCntrs *err_cntrs) {
+ m_rfc2544 = rfc2544;
+ m_err_cntrs = err_cntrs;
+}
+
+void
+RXLatency::handle_pkt(const rte_mbuf_t *m) {
+ CFlowStatParser parser;
+
+ if (m_rcv_all || parser.parse(rte_pktmbuf_mtod(m, uint8_t *), m->pkt_len) == 0) {
+ uint32_t ip_id;
+ if (m_rcv_all || (parser.get_ip_id(ip_id) == 0)) {
+ if (m_rcv_all || is_flow_stat_id(ip_id)) {
+ uint16_t hw_id;
+ if (m_rcv_all || is_flow_stat_payload_id(ip_id)) {
+ bool good_packet = true;
+ uint8_t *p = rte_pktmbuf_mtod(m, uint8_t*);
+ struct flow_stat_payload_header *fsp_head = (struct flow_stat_payload_header *)
+ (p + m->pkt_len - sizeof(struct flow_stat_payload_header));
+ hw_id = fsp_head->hw_id;
+ CRFC2544Info *curr_rfc2544;
+
+ if (unlikely(fsp_head->magic != FLOW_STAT_PAYLOAD_MAGIC) || hw_id >= MAX_FLOW_STATS_PAYLOAD) {
+ good_packet = false;
+ if (!m_rcv_all)
+ m_err_cntrs->m_bad_header++;
+ } else {
+ curr_rfc2544 = &m_rfc2544[hw_id];
+
+ if (fsp_head->flow_seq != curr_rfc2544->get_exp_flow_seq()) {
+ // bad flow seq num
+ // Might be the first packet of a new flow, packet from an old flow, or garbage.
+
+ if (fsp_head->flow_seq == curr_rfc2544->get_prev_flow_seq()) {
+ // packet from previous flow using this hw_id that arrived late
+ good_packet = false;
+ m_err_cntrs->m_old_flow++;
+ } else {
+ if (curr_rfc2544->no_flow_seq()) {
+ // first packet we see from this flow
+ good_packet = true;
+ curr_rfc2544->set_exp_flow_seq(fsp_head->flow_seq);
+ } else {
+ // garbage packet
+ good_packet = false;
+ m_err_cntrs->m_bad_header++;
+ }
+ }
+ }
+ }
+
+ if (good_packet) {
+ uint32_t pkt_seq = fsp_head->seq;
+ uint32_t exp_seq = curr_rfc2544->get_seq();
+ if (unlikely(pkt_seq != exp_seq)) {
+ if (pkt_seq < exp_seq) {
+ if (exp_seq - pkt_seq > 100000) {
+ // packet loss while we had wrap around
+ curr_rfc2544->inc_seq_err(pkt_seq - exp_seq);
+ curr_rfc2544->inc_seq_err_too_big();
+ curr_rfc2544->set_seq(pkt_seq + 1);
+ } else {
+ if (pkt_seq == (exp_seq - 1)) {
+ curr_rfc2544->inc_dup();
+ } else {
+ curr_rfc2544->inc_ooo();
+ // We thought it was lost, but it was just out of order
+ curr_rfc2544->dec_seq_err();
+ }
+ curr_rfc2544->inc_seq_err_too_low();
+ }
+ } else {
+ if (unlikely (pkt_seq - exp_seq > 100000)) {
+ // packet reorder while we had wrap around
+ if (pkt_seq == (exp_seq - 1)) {
+ curr_rfc2544->inc_dup();
+ } else {
+ curr_rfc2544->inc_ooo();
+ // We thought it was lost, but it was just out of order
+ curr_rfc2544->dec_seq_err();
+ }
+ curr_rfc2544->inc_seq_err_too_low();
+ } else {
+ // seq > curr_rfc2544->seq. Assuming lost packets
+ curr_rfc2544->inc_seq_err(pkt_seq - exp_seq);
+ curr_rfc2544->inc_seq_err_too_big();
+ curr_rfc2544->set_seq(pkt_seq + 1);
+ }
+ }
+ } else {
+ curr_rfc2544->set_seq(pkt_seq + 1);
+ }
+ m_rx_pg_stat_payload[hw_id].add_pkts(1);
+ m_rx_pg_stat_payload[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC
+ uint64_t d = (os_get_hr_tick_64() - fsp_head->time_stamp );
+ dsec_t ctime = ptime_convert_hr_dsec(d);
+ curr_rfc2544->add_sample(ctime);
+ }
+ } else {
+ hw_id = get_hw_id(ip_id);
+ if (hw_id < MAX_FLOW_STATS) {
+ m_rx_pg_stat[hw_id].add_pkts(1);
+ m_rx_pg_stat[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+RXLatency::reset_stats() {
+ for (int hw_id = 0; hw_id < MAX_FLOW_STATS; hw_id++) {
+ m_rx_pg_stat[hw_id].clear();
+ }
+}
+
+
+void
+RXLatency::get_stats(rx_per_flow_t *rx_stats,
+ int min,
+ int max,
+ bool reset,
+ TrexPlatformApi::driver_stat_cap_e type) {
+
+ for (int hw_id = min; hw_id <= max; hw_id++) {
+ if (type == TrexPlatformApi::IF_STAT_PAYLOAD) {
+ rx_stats[hw_id - min] = m_rx_pg_stat_payload[hw_id];
+ } else {
+ rx_stats[hw_id - min] = m_rx_pg_stat[hw_id];
+ }
+ if (reset) {
+ if (type == TrexPlatformApi::IF_STAT_PAYLOAD) {
+ m_rx_pg_stat_payload[hw_id].clear();
+ } else {
+ m_rx_pg_stat[hw_id].clear();
+ }
+ }
+ }
+}
+
+
+Json::Value
+RXLatency::to_json() const {
+ return Json::objectValue;
+}
+
+/**************************************
+ * RX feature queue
+ *
+ *************************************/
+
+RXPacketBuffer::RXPacketBuffer(uint64_t size) {
+ m_buffer = nullptr;
+ m_head = 0;
+ m_tail = 0;
+ m_size = (size + 1); // for the empty/full difference 1 slot reserved
+
+ /* generate queue */
+ m_buffer = new RXPacket*[m_size](); // zeroed
+}
+
+RXPacketBuffer::~RXPacketBuffer() {
+ assert(m_buffer);
+
+ while (!is_empty()) {
+ RXPacket *pkt = pop();
+ delete pkt;
+ }
+ delete [] m_buffer;
+}
+
+void
+RXPacketBuffer::push(const rte_mbuf_t *m) {
+ /* if full - pop the oldest */
+ if (is_full()) {
+ delete pop();
+ }
+
+ /* push packet */
+ m_buffer[m_head] = new RXPacket(m);
+ m_head = next(m_head);
+}
+
+RXPacket *
+RXPacketBuffer::pop() {
+ assert(!is_empty());
+
+ RXPacket *pkt = m_buffer[m_tail];
+ m_tail = next(m_tail);
+
+ return pkt;
+}
+
+uint64_t
+RXPacketBuffer::get_element_count() const {
+ if (m_head >= m_tail) {
+ return (m_head - m_tail);
+ } else {
+ return ( get_capacity() - (m_tail - m_head - 1) );
+ }
+}
+
+Json::Value
+RXPacketBuffer::to_json() const {
+
+ Json::Value output = Json::arrayValue;
+
+ int tmp = m_tail;
+ while (tmp != m_head) {
+ RXPacket *pkt = m_buffer[tmp];
+ output.append(pkt->to_json());
+ tmp = next(tmp);
+ }
+
+ return output;
+}
+
+
+void
+RXQueue::start(uint64_t size) {
+ if (m_pkt_buffer) {
+ delete m_pkt_buffer;
+ }
+ m_pkt_buffer = new RXPacketBuffer(size);
+}
+
+void
+RXQueue::stop() {
+ if (m_pkt_buffer) {
+ delete m_pkt_buffer;
+ m_pkt_buffer = NULL;
+ }
+}
+
+const RXPacketBuffer *
+RXQueue::fetch() {
+
+ /* if no buffer or the buffer is empty - give a NULL one */
+ if ( (!m_pkt_buffer) || (m_pkt_buffer->get_element_count() == 0) ) {
+ return nullptr;
+ }
+
+ /* hold a pointer to the old one */
+ RXPacketBuffer *old_buffer = m_pkt_buffer;
+
+ /* replace the old one with a new one and freeze the old */
+ m_pkt_buffer = new RXPacketBuffer(old_buffer->get_capacity());
+
+ return old_buffer;
+}
+
+void
+RXQueue::handle_pkt(const rte_mbuf_t *m) {
+ m_pkt_buffer->push(m);
+}
+
+Json::Value
+RXQueue::to_json() const {
+ assert(m_pkt_buffer != NULL);
+
+ Json::Value output = Json::objectValue;
+
+ output["size"] = Json::UInt64(m_pkt_buffer->get_capacity());
+ output["count"] = Json::UInt64(m_pkt_buffer->get_element_count());
+
+ return output;
+}
+
+/**************************************
+ * RX feature recorder
+ *
+ *************************************/
+
+RXPacketRecorder::RXPacketRecorder() {
+ m_writer = NULL;
+ m_count = 0;
+ m_limit = 0;
+ m_epoch = -1;
+
+ m_pending_flush = false;
+}
+
+void
+RXPacketRecorder::start(const std::string &pcap, uint64_t limit) {
+ m_writer = CCapWriterFactory::CreateWriter(LIBPCAP, (char *)pcap.c_str());
+ if (m_writer == NULL) {
+ std::stringstream ss;
+ ss << "unable to create PCAP file: " << pcap;
+ throw TrexException(ss.str());
+ }
+
+ assert(limit > 0);
+
+ m_limit = limit;
+ m_count = 0;
+ m_pending_flush = false;
+ m_pcap_filename = pcap;
+}
+
+void
+RXPacketRecorder::stop() {
+ if (!m_writer) {
+ return;
+ }
+
+ delete m_writer;
+ m_writer = NULL;
+}
+
+void
+RXPacketRecorder::flush_to_disk() {
+
+ if (m_writer && m_pending_flush) {
+ m_writer->flush_to_disk();
+ m_pending_flush = false;
+ }
+}
+
+void
+RXPacketRecorder::handle_pkt(const rte_mbuf_t *m) {
+ if (!m_writer) {
+ return;
+ }
+
+ dsec_t now = now_sec();
+ if (m_epoch < 0) {
+ m_epoch = now;
+ }
+
+ dsec_t dt = now - m_epoch;
+
+ CPktNsecTimeStamp t_c(dt);
+ m_pkt.time_nsec = t_c.m_time_nsec;
+ m_pkt.time_sec = t_c.m_time_sec;
+
+ const uint8_t *p = rte_pktmbuf_mtod(m, uint8_t *);
+ m_pkt.pkt_len = m->pkt_len;
+ memcpy(m_pkt.raw, p, m->pkt_len);
+
+ m_writer->write_packet(&m_pkt);
+ m_count++;
+ m_pending_flush = true;
+
+ if (m_count == m_limit) {
+ stop();
+ }
+
+}
+
+Json::Value
+RXPacketRecorder::to_json() const {
+ Json::Value output = Json::objectValue;
+
+ output["pcap_filename"] = m_pcap_filename;
+ output["limit"] = Json::UInt64(m_limit);
+ output["count"] = Json::UInt64(m_count);
+
+ return output;
+}
+
+
+/**************************************
+ * RX feature server (ARP, ICMP) and etc.
+ *
+ *************************************/
+
+class RXPktParser {
+public:
+ RXPktParser(const rte_mbuf_t *m) {
+
+ m_mbuf = m;
+
+ /* start point */
+ m_current = rte_pktmbuf_mtod(m, uint8_t *);;
+ m_size_left = rte_pktmbuf_pkt_len(m);
+
+ m_ether = NULL;
+ m_arp = NULL;
+ m_ipv4 = NULL;
+ m_icmp = NULL;
+ m_vlan_tag = 0;
+
+ /* ethernet */
+ m_ether = (EthernetHeader *)parse_bytes(14);
+
+ uint16_t next_proto;
+ if (m_ether->getNextProtocol() == EthernetHeader::Protocol::VLAN) {
+ parse_bytes(4);
+ m_vlan_tag = m_ether->getVlanTag();
+ next_proto = m_ether->getVlanProtocol();
+ } else {
+ next_proto = m_ether->getNextProtocol();
+ }
+
+ /**
+ * support only for ARP or IPv4 based protocols
+ */
+ switch (next_proto) {
+ case EthernetHeader::Protocol::ARP:
+ parse_arp();
+ return;
+
+ case EthernetHeader::Protocol::IP:
+ parse_ipv4();
+ return;
+
+ default:
+ return;
+ }
+
+ }
+
+ const rte_mbuf_t *m_mbuf;
+ EthernetHeader *m_ether;
+ ArpHdr *m_arp;
+ IPHeader *m_ipv4;
+ ICMPHeader *m_icmp;
+ uint16_t m_vlan_tag;
+
+protected:
+
+ const uint8_t *parse_bytes(uint32_t size) {
+ if (m_size_left < size) {
+ parse_err();
+ }
+
+ const uint8_t *p = m_current;
+ m_current += size;
+ m_size_left -= size;
+
+ return p;
+ }
+
+ void parse_arp() {
+ m_arp = (ArpHdr *)parse_bytes(sizeof(ArpHdr));
+ }
+
+ void parse_ipv4() {
+ m_ipv4 = (IPHeader *)parse_bytes(IPHeader::DefaultSize);
+
+ /* advance over IP options if exists */
+ parse_bytes(m_ipv4->getOptionLen());
+
+ switch (m_ipv4->getNextProtocol()) {
+ case IPHeader::Protocol::ICMP:
+ parse_icmp();
+ return;
+
+ default:
+ return;
+ }
+ }
+
+ void parse_icmp() {
+ m_icmp = (ICMPHeader *)parse_bytes(sizeof(ICMPHeader));
+ }
+
+ void parse_err() {
+ throw TrexException(TrexException::T_RX_PKT_PARSE_ERR);
+ }
+
+ const uint8_t *m_current;
+ uint16_t m_size_left;
+};
+
+RXServer::RXServer() {
+ m_io = NULL;
+ m_src_addr = NULL;
+ m_port_id = 255;
+}
+
+void
+RXServer::create(uint8_t port_id, CPortLatencyHWBase *io, const CManyIPInfo *src_addr) {
+ m_port_id = port_id;
+ m_io = io;
+ m_src_addr = src_addr;
+}
+
+
+void
+RXServer::handle_pkt(const rte_mbuf_t *m) {
+
+ RXPktParser parser(m);
+
+ if (parser.m_icmp) {
+ handle_icmp(parser);
+ } else if (parser.m_arp) {
+ handle_arp(parser);
+ } else {
+ return;
+ }
+
+}
+void
+RXServer::handle_icmp(RXPktParser &parser) {
+
+ /* maybe not for us... */
+ if (!m_src_addr->exists(parser.m_ipv4->getDestIp())) {
+ return;
+ }
+
+ /* we handle only echo request */
+ if (parser.m_icmp->getType() != ICMPHeader::TYPE_ECHO_REQUEST) {
+ return;
+ }
+
+ /* duplicate the MBUF */
+ rte_mbuf_t *response = duplicate_mbuf(parser.m_mbuf);
+ if (!response) {
+ return;
+ }
+
+ /* reparse the cloned packet */
+ RXPktParser response_parser(response);
+
+ /* swap MAC */
+ MacAddress tmp = response_parser.m_ether->mySource;
+ response_parser.m_ether->mySource = response_parser.m_ether->myDestination;
+ response_parser.m_ether->myDestination = tmp;
+
+ /* swap IP */
+ std::swap(response_parser.m_ipv4->mySource, response_parser.m_ipv4->myDestination);
+
+ /* new packet - new TTL */
+ response_parser.m_ipv4->setTimeToLive(128);
+ response_parser.m_ipv4->updateCheckSum();
+
+ /* update type and fix checksum */
+ response_parser.m_icmp->setType(ICMPHeader::TYPE_ECHO_REPLY);
+ response_parser.m_icmp->updateCheckSum(response_parser.m_ipv4->getTotalLength() - response_parser.m_ipv4->getHeaderLength());
+
+ /* send */
+ m_io->tx(response);
+}
+
+void
+RXServer::handle_arp(RXPktParser &parser) {
+ MacAddress src_mac;
+
+ /* only ethernet format supported */
+ if (parser.m_arp->getHrdType() != ArpHdr::ARP_HDR_HRD_ETHER) {
+ return;
+ }
+
+ /* IPV4 only */
+ if (parser.m_arp->getProtocolType() != ArpHdr::ARP_HDR_PROTO_IPV4) {
+ return;
+ }
+
+ /* support only for ARP request */
+ if (parser.m_arp->getOp() != ArpHdr::ARP_HDR_OP_REQUEST) {
+ return;
+ }
+
+ /* are we the target ? if not - go home */
+ if (!m_src_addr->lookup(parser.m_arp->getTip(), 0, src_mac)) {
+ return;
+ }
+
+ /* duplicate the MBUF */
+ rte_mbuf_t *response = duplicate_mbuf(parser.m_mbuf);
+ if (!response) {
+ return;
+ }
+
+ /* reparse the cloned packet */
+ RXPktParser response_parser(response);
+
+ /* reply */
+ response_parser.m_arp->setOp(ArpHdr::ARP_HDR_OP_REPLY);
+
+ /* fix the MAC addresses */
+ response_parser.m_ether->mySource = src_mac;
+ response_parser.m_ether->myDestination = parser.m_ether->mySource;
+
+ /* fill up the fields */
+
+ /* src */
+ response_parser.m_arp->m_arp_sha = src_mac;
+ response_parser.m_arp->setSip(parser.m_arp->getTip());
+
+ /* dst */
+ response_parser.m_arp->m_arp_tha = parser.m_arp->m_arp_sha;
+ response_parser.m_arp->m_arp_tip = parser.m_arp->m_arp_sip;
+
+ /* send */
+ m_io->tx(response);
+
+}
+
+rte_mbuf_t *
+RXServer::duplicate_mbuf(const rte_mbuf_t *m) {
+ /* RX packets should always be one segment */
+ assert(m->nb_segs == 1);
+
+ /* allocate */
+ rte_mbuf_t *clone_mbuf = CGlobalInfo::pktmbuf_alloc_by_port(m_port_id, rte_pktmbuf_pkt_len(m));
+ if (!clone_mbuf) {
+ return NULL;
+ }
+
+ /* append data */
+ uint8_t *dest = (uint8_t *)rte_pktmbuf_append(clone_mbuf, rte_pktmbuf_pkt_len(m));
+ if (!dest) {
+ return NULL;
+ }
+
+ /* copy data */
+ const uint8_t *src = rte_pktmbuf_mtod(m, const uint8_t *);
+ memcpy(dest, src, rte_pktmbuf_pkt_len(m));
+
+ return clone_mbuf;
+}
+
+/**************************************
+ * Gratidious ARP
+ *
+ *************************************/
+void
+RXGratARP::create(uint8_t port_id,
+ CPortLatencyHWBase *io,
+ CManyIPInfo *src_addr,
+ CRXCoreIgnoreStat *ign_stats) {
+
+ m_port_id = port_id;
+ m_io = io;
+ m_src_addr = src_addr;
+ m_ign_stats = ign_stats;
+}
+
+void
+RXGratARP::send_next_grat_arp() {
+ uint8_t src_mac[ETHER_ADDR_LEN];
+
+ const COneIPInfo *ip_info = m_src_addr->get_next_loop();
+ if (!ip_info) {
+ return;
+ }
+
+ rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc_small(CGlobalInfo::m_socket.port_to_socket(m_port_id));
+ assert(m);
+
+ uint8_t *p = (uint8_t *)rte_pktmbuf_append(m, ip_info->get_grat_arp_len());
+ ip_info->get_mac(src_mac);
+ uint16_t vlan = ip_info->get_vlan();
+
+ /* for now only IPv4 */
+ assert(ip_info->ip_ver() == COneIPInfo::IP4_VER);
+ uint32_t sip = ((COneIPv4Info *)ip_info)->get_ip();
+
+ CTestPktGen::create_arp_req(p, sip, sip, src_mac, vlan, m_port_id);
+
+ if (m_io->tx(m) == 0) {
+ m_ign_stats->m_tx_arp += 1;
+ m_ign_stats->m_tot_bytes += 64;
+ }
+
+}
+
+Json::Value
+RXGratARP::to_json() const {
+ Json::Value output = Json::objectValue;
+ output["interval_sec"] = (double)CGlobalInfo::m_options.m_arp_ref_per;
+
+ return output;
+}
+
+/**************************************
+ * Port manager
+ *
+ *************************************/
+
+RXPortManager::RXPortManager() {
+ clear_all_features();
+ m_io = NULL;
+ m_cpu_dp_u = NULL;
+ m_port_id = UINT8_MAX;
+}
+
+
+void
+RXPortManager::create(const TRexPortAttr *port_attr,
+ CPortLatencyHWBase *io,
+ CRFC2544Info *rfc2544,
+ CRxCoreErrCntrs *err_cntrs,
+ CCpuUtlDp *cpu_util,
+ uint8_t crc_bytes_num) {
+
+ m_port_id = port_attr->get_port_id();
+ m_io = io;
+ m_cpu_dp_u = cpu_util;
+ m_num_crc_fix_bytes = crc_bytes_num;
+
+ /* init features */
+ m_latency.create(rfc2544, err_cntrs);
+ m_server.create(m_port_id, io, &m_src_addr);
+ m_grat_arp.create(m_port_id, io, &m_src_addr, &m_ign_stats);
+
+ /* by default, server is always on */
+ set_feature(SERVER);
+}
+
+void RXPortManager::handle_pkt(const rte_mbuf_t *m) {
+
+ /* handle features */
+
+ if (is_feature_set(LATENCY)) {
+ m_latency.handle_pkt(m);
+ }
+
+ if (is_feature_set(RECORDER)) {
+ m_recorder.handle_pkt(m);
+ }
+
+ if (is_feature_set(QUEUE)) {
+ m_queue.handle_pkt(m);
+ }
+
+ if (is_feature_set(SERVER)) {
+ m_server.handle_pkt(m);
+ }
+}
+
+int RXPortManager::process_all_pending_pkts(bool flush_rx) {
+
+ rte_mbuf_t *rx_pkts[64];
+
+ /* try to read 64 packets clean up the queue */
+ uint16_t cnt_p = m_io->rx_burst(rx_pkts, 64);
+ if (cnt_p == 0) {
+ return cnt_p;
+ }
+
+
+ m_cpu_dp_u->start_work1();
+
+ for (int j = 0; j < cnt_p; j++) {
+ rte_mbuf_t *m = rx_pkts[j];
+
+ if (!flush_rx) {
+ // patch relevant only for e1000 driver
+ if (m_num_crc_fix_bytes) {
+ rte_pktmbuf_trim(m, m_num_crc_fix_bytes);
+ }
+
+ handle_pkt(m);
+ }
+
+ rte_pktmbuf_free(m);
+ }
+
+ /* commit only if there was work to do ! */
+ m_cpu_dp_u->commit1();
+
+
+ return cnt_p;
+}
+
+void
+RXPortManager::tick() {
+ if (is_feature_set(RECORDER)) {
+ m_recorder.flush_to_disk();
+ }
+}
+
+void
+RXPortManager::send_next_grat_arp() {
+ if (is_feature_set(GRAT_ARP)) {
+ m_grat_arp.send_next_grat_arp();
+ }
+}
+
+
+void
+RXPortManager::set_l2_mode() {
+
+ /* no IPv4 addresses */
+ m_src_addr.clear();
+
+ /* stop grat arp */
+ stop_grat_arp();
+}
+
+void
+RXPortManager::set_l3_mode(const CManyIPInfo &ip_info, bool is_grat_arp_needed) {
+
+ /* copy L3 address */
+ m_src_addr = ip_info;
+
+ if (is_grat_arp_needed) {
+ start_grat_arp();
+ }
+ else {
+ stop_grat_arp();
+ }
+
+}
+
+
+
+Json::Value
+RXPortManager::to_json() const {
+ Json::Value output = Json::objectValue;
+
+ if (is_feature_set(LATENCY)) {
+ output["latency"] = m_latency.to_json();
+ output["latency"]["is_active"] = true;
+ } else {
+ output["latency"]["is_active"] = false;
+ }
+
+ if (is_feature_set(RECORDER)) {
+ output["sniffer"] = m_recorder.to_json();
+ output["sniffer"]["is_active"] = true;
+ } else {
+ output["sniffer"]["is_active"] = false;
+ }
+
+ if (is_feature_set(QUEUE)) {
+ output["queue"] = m_queue.to_json();
+ output["queue"]["is_active"] = true;
+ } else {
+ output["queue"]["is_active"] = false;
+ }
+
+ if (is_feature_set(GRAT_ARP)) {
+ output["grat_arp"] = m_grat_arp.to_json();
+ output["grat_arp"]["is_active"] = true;
+ } else {
+ output["grat_arp"]["is_active"] = false;
+ }
+
+ return output;
+}
+
+
+void RXPortManager::get_ignore_stats(CRXCoreIgnoreStat &stat, bool get_diff) {
+ if (get_diff) {
+ stat = m_ign_stats - m_ign_stats_prev;
+ m_ign_stats_prev = m_ign_stats;
+ } else {
+ stat = m_ign_stats;
+ }
+}
+
diff --git a/src/stateless/rx/trex_stateless_rx_port_mngr.h b/src/stateless/rx/trex_stateless_rx_port_mngr.h
new file mode 100644
index 00000000..8947def7
--- /dev/null
+++ b/src/stateless/rx/trex_stateless_rx_port_mngr.h
@@ -0,0 +1,517 @@
+/*
+ Itay Marom
+ Cisco Systems, Inc.
+*/
+
+/*
+ Copyright (c) 2016-2016 Cisco Systems, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef __TREX_STATELESS_RX_PORT_MNGR_H__
+#define __TREX_STATELESS_RX_PORT_MNGR_H__
+
+#include <stdint.h>
+#include "common/base64.h"
+
+#include "common/captureFile.h"
+
+
+class CPortLatencyHWBase;
+class CRFC2544Info;
+class CRxCoreErrCntrs;
+
+/**************************************
+ * RX feature latency
+ *
+ *************************************/
+class RXLatency {
+public:
+
+ RXLatency();
+
+ void create(CRFC2544Info *rfc2544, CRxCoreErrCntrs *err_cntrs);
+
+ void handle_pkt(const rte_mbuf_t *m);
+
+ Json::Value to_json() const;
+
+ void get_stats(rx_per_flow_t *rx_stats,
+ int min,
+ int max,
+ bool reset,
+ TrexPlatformApi::driver_stat_cap_e type);
+
+ void reset_stats();
+
+private:
+ bool is_flow_stat_id(uint32_t id) {
+ if ((id & 0x000fff00) == IP_ID_RESERVE_BASE) return true;
+ return false;
+ }
+
+ bool is_flow_stat_payload_id(uint32_t id) {
+ if (id == FLOW_STAT_PAYLOAD_IP_ID) return true;
+ return false;
+ }
+
+ uint16_t get_hw_id(uint16_t id) {
+ return (0x00ff & id);
+}
+
+public:
+
+ rx_per_flow_t m_rx_pg_stat[MAX_FLOW_STATS];
+ rx_per_flow_t m_rx_pg_stat_payload[MAX_FLOW_STATS_PAYLOAD];
+
+ bool m_rcv_all;
+ CRFC2544Info *m_rfc2544;
+ CRxCoreErrCntrs *m_err_cntrs;
+};
+
+/**
+ * describes a single saved RX packet
+ *
+ */
+class RXPacket {
+public:
+
+ RXPacket(const rte_mbuf_t *m) {
+ /* assume single part packet */
+ assert(m->nb_segs == 1);
+
+ m_size = m->pkt_len;
+ const uint8_t *p = rte_pktmbuf_mtod(m, uint8_t *);
+
+ m_raw = new uint8_t[m_size];
+ memcpy(m_raw, p, m_size);
+
+ /* generate a packet timestamp */
+ m_timestamp = now_sec();
+ }
+
+ /* slow path and also RVO - pass by value is ok */
+ Json::Value to_json() {
+ Json::Value output;
+ output["ts"] = m_timestamp;
+ output["binary"] = base64_encode(m_raw, m_size);
+ return output;
+ }
+
+ ~RXPacket() {
+ if (m_raw) {
+ delete [] m_raw;
+ }
+ }
+
+private:
+
+ uint8_t *m_raw;
+ uint16_t m_size;
+ dsec_t m_timestamp;
+};
+
+
+/**************************************
+ * RX feature queue
+ *
+ *************************************/
+
+class RXPacketBuffer {
+public:
+
+ RXPacketBuffer(uint64_t size);
+ ~RXPacketBuffer();
+
+ /**
+ * push a packet to the buffer
+ *
+ */
+ void push(const rte_mbuf_t *m);
+
+ /**
+ * generate a JSON output of the queue
+ *
+ */
+ Json::Value to_json() const;
+
+
+ bool is_empty() const {
+ return (m_head == m_tail);
+ }
+
+ bool is_full() const {
+ return ( next(m_head) == m_tail);
+ }
+
+ /**
+ * return the total amount of space possible
+ */
+ uint64_t get_capacity() const {
+ /* one slot is used for diff between full/empty */
+ return (m_size - 1);
+ }
+
+ /**
+ * returns how many elements are in the queue
+ */
+ uint64_t get_element_count() const;
+
+private:
+ int next(int v) const {
+ return ( (v + 1) % m_size );
+ }
+
+ /* pop in case of full queue - internal usage */
+ RXPacket * pop();
+
+ int m_head;
+ int m_tail;
+ int m_size;
+ RXPacket **m_buffer;
+};
+
+
+class RXQueue {
+public:
+ RXQueue() {
+ m_pkt_buffer = nullptr;
+ }
+
+ ~RXQueue() {
+ stop();
+ }
+
+ /**
+ * start RX queue
+ *
+ */
+ void start(uint64_t size);
+
+ /**
+ * fetch the current buffer
+ * return NULL if no packets
+ */
+ const RXPacketBuffer * fetch();
+
+ /**
+ * stop RX queue
+ *
+ */
+ void stop();
+
+ void handle_pkt(const rte_mbuf_t *m);
+
+ Json::Value to_json() const;
+
+private:
+ RXPacketBuffer *m_pkt_buffer;
+};
+
+/**************************************
+ * RX feature PCAP recorder
+ *
+ *************************************/
+
+class RXPacketRecorder {
+public:
+ RXPacketRecorder();
+
+ ~RXPacketRecorder() {
+ stop();
+ }
+
+ void start(const std::string &pcap, uint64_t limit);
+ void stop();
+ void handle_pkt(const rte_mbuf_t *m);
+
+ /**
+ * flush any cached packets to disk
+ *
+ */
+ void flush_to_disk();
+
+ Json::Value to_json() const;
+
+private:
+ CFileWriterBase *m_writer;
+ std::string m_pcap_filename;
+ CCapPktRaw m_pkt;
+ dsec_t m_epoch;
+ uint64_t m_limit;
+ uint64_t m_count;
+ bool m_pending_flush;
+};
+
+
+/**************************************
+ * RX server (ping, ARP and etc.)
+ *
+ *************************************/
+class RXPktParser;
+class RXServer {
+public:
+
+ RXServer();
+ void create(uint8_t port_id, CPortLatencyHWBase *io, const CManyIPInfo *src_addr);
+ void handle_pkt(const rte_mbuf_t *m);
+
+private:
+ void handle_icmp(RXPktParser &parser);
+ void handle_arp(RXPktParser &parser);
+ rte_mbuf_t *duplicate_mbuf(const rte_mbuf_t *m);
+
+ CPortLatencyHWBase *m_io;
+ uint8_t m_port_id;
+ const CManyIPInfo *m_src_addr;
+};
+
+/**************************************
+ * Gratidious ARP
+ *
+ *************************************/
+class RXGratARP {
+public:
+ RXGratARP() {
+ m_io = NULL;
+ m_port_id = UINT8_MAX;
+ m_src_addr = NULL;
+ m_ign_stats = NULL;
+ }
+
+ void create(uint8_t port_id,
+ CPortLatencyHWBase *io,
+ CManyIPInfo *src_addr,
+ CRXCoreIgnoreStat *ignore_stats);
+
+
+ /**
+ * the main 'tick' of the service
+ *
+ */
+ void send_next_grat_arp();
+
+ Json::Value to_json() const;
+
+private:
+ uint8_t m_port_id;
+ CPortLatencyHWBase *m_io;
+ CManyIPInfo *m_src_addr;
+ CRXCoreIgnoreStat *m_ign_stats;
+};
+
+/************************ manager ***************************/
+
+/**
+ * per port RX features manager
+ *
+ * @author imarom (10/30/2016)
+ */
+class RXPortManager {
+public:
+ enum feature_t {
+ NO_FEATURES = 0x0,
+ LATENCY = 0x1,
+ RECORDER = 0x2,
+ QUEUE = 0x4,
+ SERVER = 0x8,
+ GRAT_ARP = 0x10,
+ };
+
+ RXPortManager();
+
+ void create(const TRexPortAttr *port_attr,
+ CPortLatencyHWBase *io,
+ CRFC2544Info *rfc2544,
+ CRxCoreErrCntrs *err_cntrs,
+ CCpuUtlDp *cpu_util,
+ uint8_t crc_bytes_num);
+
+
+ void clear_stats() {
+ m_latency.reset_stats();
+ }
+
+
+ void get_latency_stats(rx_per_flow_t *rx_stats,
+ int min,
+ int max,
+ bool reset,
+ TrexPlatformApi::driver_stat_cap_e type) {
+
+ return m_latency.get_stats(rx_stats, min, max, reset, type);
+ }
+
+ RXLatency & get_latency() {
+ return m_latency;
+ }
+
+ /* latency */
+ void enable_latency() {
+ set_feature(LATENCY);
+ }
+
+ void disable_latency() {
+ unset_feature(LATENCY);
+ }
+
+ /* recorder */
+ void start_recorder(const std::string &pcap, uint64_t limit_pkts) {
+ m_recorder.start(pcap, limit_pkts);
+ set_feature(RECORDER);
+ }
+
+ void stop_recorder() {
+ m_recorder.stop();
+ unset_feature(RECORDER);
+ }
+
+ /* queue */
+ void start_queue(uint32_t size) {
+ m_queue.start(size);
+ set_feature(QUEUE);
+ }
+
+ void stop_queue() {
+ m_queue.stop();
+ unset_feature(QUEUE);
+ }
+
+ const RXPacketBuffer *get_pkt_buffer() {
+ if (!is_feature_set(QUEUE)) {
+ return nullptr;
+ }
+
+ return m_queue.fetch();
+ }
+
+ void start_grat_arp() {
+ set_feature(GRAT_ARP);
+ }
+
+ void stop_grat_arp() {
+ unset_feature(GRAT_ARP);
+ }
+
+ /**
+ * fetch and process all packets
+ *
+ */
+ int process_all_pending_pkts(bool flush_rx = false);
+
+
+ /**
+ * flush all pending packets without processing them
+ *
+ */
+ void flush_all_pending_pkts() {
+ process_all_pending_pkts(true);
+ }
+
+
+ /**
+ * handle a single packet
+ *
+ */
+ void handle_pkt(const rte_mbuf_t *m);
+
+ /**
+ * maintenance
+ *
+ * @author imarom (11/24/2016)
+ */
+ void tick();
+
+ /**
+ * send next grat arp (if on)
+ *
+ * @author imarom (12/13/2016)
+ */
+ void send_next_grat_arp();
+
+ /**
+ * set port mode to L2
+ */
+ void set_l2_mode();
+
+ /**
+ * set port mode to L3
+ *
+ * @author imarom (12/13/2016)
+ */
+ void set_l3_mode(const CManyIPInfo &ip_info, bool is_grat_arp_needed);
+
+
+ bool has_features_set() {
+ return (m_features != NO_FEATURES);
+ }
+
+
+ bool no_features_set() {
+ return (!has_features_set());
+ }
+
+ /**
+ * returns ignored set of stats
+ * (grat ARP, PING response and etc.)
+ */
+ void get_ignore_stats(CRXCoreIgnoreStat &stat, bool get_diff);
+
+ /**
+ * write the status to a JSON format
+ */
+ Json::Value to_json() const;
+
+private:
+
+ void clear_all_features() {
+ m_features = NO_FEATURES;
+ }
+
+ void set_feature(feature_t feature) {
+ m_features |= feature;
+ }
+
+ void unset_feature(feature_t feature) {
+ m_features &= (~feature);
+ }
+
+ bool is_feature_set(feature_t feature) const {
+ return ( (m_features & feature) == feature );
+ }
+
+ uint32_t m_features;
+ uint8_t m_port_id;
+ RXLatency m_latency;
+ RXPacketRecorder m_recorder;
+ RXQueue m_queue;
+ RXServer m_server;
+ RXGratARP m_grat_arp;
+
+ // compensate for the fact that hardware send us packets without Ethernet CRC, and we report with it
+ uint8_t m_num_crc_fix_bytes;
+
+ CCpuUtlDp *m_cpu_dp_u;
+ CPortLatencyHWBase *m_io;
+ CManyIPInfo m_src_addr;
+
+ /* stats to ignore (ARP and etc.) */
+ CRXCoreIgnoreStat m_ign_stats;
+ CRXCoreIgnoreStat m_ign_stats_prev;
+};
+
+
+
+#endif /* __TREX_STATELESS_RX_PORT_MNGR_H__ */
+
diff --git a/src/trex_client_config.cpp b/src/trex_client_config.cpp
index ad71d38e..548d097b 100644
--- a/src/trex_client_config.cpp
+++ b/src/trex_client_config.cpp
@@ -30,38 +30,200 @@ limitations under the License.
#include "common/basic_utils.h"
#include "bp_sim.h"
+void ClientCfgDirBase::dump(FILE *fd) const {
+ if (has_src_mac_addr()) {
+ fprintf(fd, " src_mac: %s\n", utl_macaddr_to_str(m_src_mac.GetConstBuffer()).c_str());
+ } else {
+ fprintf(fd, "# No src MAC\n");
+ }
+ if (has_dst_mac_addr()) {
+ fprintf(fd, " dst_mac: %s\n", utl_macaddr_to_str(m_dst_mac.GetConstBuffer()).c_str());
+ } else {
+ fprintf(fd, "# No dst MAC\n");
+ }
+ if (has_vlan()) {
+ fprintf(fd, " vlan: %d\n", m_vlan);
+ } else {
+ fprintf(fd, "# No vlan\n");
+ }
+}
+
+void ClientCfgDirBase::update(uint32_t index, const ClientCfgDirExt &cfg) {
+ if (has_src_mac_addr()) {
+ m_src_mac += index;
+ }
+
+ if (has_dst_mac_addr() || cfg.has_next_hop() || cfg.has_ipv6_next_hop()) {
+ m_dst_mac = cfg.get_resolved_mac(index);
+ m_bitfield |= HAS_DST_MAC;
+ }
+}
+
+bool ClientCfgDirExt::need_resolve() const {
+ if (has_next_hop() || has_ipv6_next_hop())
+ return true;
+ else
+ return false;
+}
+
+void ClientCfgDirExt::set_no_resolve_needed() {
+ m_bitfield &= ~(HAS_DST_MAC | HAS_IPV6_NEXT_HOP | HAS_NEXT_HOP);
+}
+
+void ClientCfgDirExt::dump(FILE *fd) const {
+ ClientCfgDirBase::dump(fd);
+
+ if (has_next_hop()) {
+ fprintf(fd, " next_hop: %s\n", ip_to_str(m_next_hop).c_str());
+ } else {
+ fprintf(fd, "# No next hop\n");
+ }
+ if (has_ipv6_next_hop()) {
+ fprintf(fd, " ipv6_next_hop: %s\n", ip_to_str((unsigned char *)m_ipv6_next_hop).c_str());
+ } else {
+ fprintf(fd, "# No IPv6 next hop\n");
+ }
+
+ if (m_resolved_macs.size() > 0) {
+ fprintf(fd, "# Resolved MAC list:\n");
+ for (int i = 0; i < m_resolved_macs.size(); i++) {
+ fprintf(fd, "# %s\n", utl_macaddr_to_str(m_resolved_macs[i].GetConstBuffer()).c_str());
+ }
+ }
+}
+
+void ClientCfgDirExt::set_resolved_macs(CManyIPInfo &pretest_result, uint16_t count) {
+ uint16_t vlan = has_vlan() ? m_vlan : 0;
+ MacAddress base_mac = m_dst_mac;
+ m_resolved_macs.resize(count);
+
+ for (int i = 0; i < count; i++) {
+ if (need_resolve()) {
+ if (has_next_hop()) {
+ if (!pretest_result.lookup(m_next_hop + i, vlan, m_resolved_macs[i])) {
+ fprintf(stderr, "Failed resolving ip:%x, vlan:%d - exiting\n", m_next_hop+i, vlan);
+ exit(1);
+ }
+ } else {
+ //??? handle ipv6
+ }
+ } else {
+ m_resolved_macs[i] = base_mac;
+ base_mac += 1;
+ }
+ }
+}
+
+void ClientCfgBase::update(uint32_t index, const ClientCfgExt *cfg) {
+ m_initiator.update(index, cfg->m_initiator);
+ m_responder.update(index, cfg->m_responder);
+}
+
void
-ClientCfgEntry::dump() const {
+ClientCfgEntry::dump(FILE *fd) const {
- std::cout << "IP start: " << ip_to_str(m_ip_start) << "\n";
- std::cout << "IP end: " << ip_to_str(m_ip_end) << "\n";
+ fprintf(fd, "- ip_start : %s\n", ip_to_str(m_ip_start).c_str());
+ fprintf(fd, " ip_end : %s\n", ip_to_str(m_ip_end).c_str());
+ m_cfg.dump(fd);
+ fprintf(fd, " count : %d\n", m_count);
+}
- //m_cfg.dump();
+void ClientCfgEntry::set_resolved_macs(CManyIPInfo &pretest_result) {
+ m_cfg.m_initiator.set_resolved_macs(pretest_result, m_count);
+ m_cfg.m_responder.set_resolved_macs(pretest_result, m_count);
+}
- #if 0
- std::cout << "Init. MAC addr: ";
- for (int i = 0; i < 6; i++) {
- printf("%lx:", ( (m_initiator.m_dst_mac >> ( (6-i) * 8)) & 0xFF ) );
+void ClientCfgCompactEntry::fill_from_dir(ClientCfgDirExt cfg, uint8_t port_id) {
+ m_port = port_id;
+ if (cfg.has_next_hop()) {
+ m_next_hop_base.ip = cfg.m_next_hop;
+ if (cfg.has_src_ip()) {
+ m_src_ip.ip = cfg.m_src_ip;
+ } else {
+ m_src_ip.ip = 0;
+ }
+ m_is_ipv4 = true;
+ } else if (cfg.has_ipv6_next_hop()) {
+ memcpy(m_next_hop_base.ipv6, cfg.m_ipv6_next_hop, sizeof(m_next_hop_base.ipv6));
+ if (cfg.has_src_ipv6()) {
+ memcpy(m_src_ip.ipv6, cfg.m_src_ipv6, sizeof(m_src_ip.ipv6));
+ } else {
+ memset(m_src_ip.ipv6, 0, sizeof(m_src_ip.ipv6));
+ }
+ m_is_ipv4 = false;
}
- std::cout << "\n";
- std::cout << "Init. VLAN: " << m_initiator.m_vlan << "\n";
+ if (cfg.has_vlan()) {
+ m_vlan = cfg.m_vlan;
+ } else {
+ m_vlan = 0;
+ }
+}
- std::cout << "Res. MAC addr: ";
- for (int i = 0; i < 6; i++) {
- printf("%lx:", ( (m_responder.m_dst_mac >> ( (6-i) * 8)) & 0xFF ) );
+void
+ClientCfgDB::dump(FILE *fd) {
+ //fprintf(fd, "#**********Client config file start*********\n");
+ fprintf(fd, "vlan: %s\n", m_under_vlan ? "true" : "false");
+ fprintf(fd, "groups:\n");
+
+ for (std::map<uint32_t, ClientCfgEntry>::iterator it = m_groups.begin(); it != m_groups.end(); ++it) {
+ fprintf(fd, "# ****%s:****\n", ip_to_str(it->first).c_str());
+ ((ClientCfgEntry)it->second).dump(fd);
}
- std::cout << "\n";
+ //fprintf(fd, "#**********Client config end*********\n");
+}
- std::cout << "Res. VLAN: " << m_responder.m_vlan << "\n";
- #endif
+void ClientCfgDB::set_resolved_macs(CManyIPInfo &pretest_result) {
+ std::map<uint32_t, ClientCfgEntry>::iterator it;
+ for (it = m_groups.begin(); it != m_groups.end(); it++) {
+ ClientCfgEntry &cfg = it->second;
+ cfg.set_resolved_macs(pretest_result);
+ }
}
+void ClientCfgDB::get_entry_list(std::vector<ClientCfgCompactEntry *> &ret) {
+ uint8_t port;
+ bool result;
+
+ assert(m_tg != NULL);
+ for (std::map<uint32_t, ClientCfgEntry>::iterator it = m_groups.begin(); it != m_groups.end(); ++it) {
+ ClientCfgEntry &cfg = it->second;
+ if (cfg.m_cfg.m_initiator.need_resolve() || cfg.m_cfg.m_initiator.need_resolve()) {
+ result = m_tg->find_port(cfg.m_ip_start, cfg.m_ip_end, port);
+ if (! result) {
+ fprintf(stderr, "Error in clinet config range %s - %s.\n"
+ , ip_to_str(cfg.m_ip_start).c_str(), ip_to_str(cfg.m_ip_end).c_str());
+ exit(-1);
+ }
+ if (port == UINT8_MAX) {
+ // if port not found, it means this adderss is not needed. Don't try to resolve.
+ cfg.m_cfg.m_initiator.set_no_resolve_needed();
+ cfg.m_cfg.m_responder.set_no_resolve_needed();
+ } else {
+ if (cfg.m_cfg.m_initiator.need_resolve()) {
+ ClientCfgCompactEntry *init_entry = new ClientCfgCompactEntry();
+ assert(init_entry);
+ init_entry->m_count = cfg.m_count;
+ init_entry->fill_from_dir(cfg.m_cfg.m_initiator, port);
+ ret.push_back(init_entry);
+ }
+
+ if (cfg.m_cfg.m_responder.need_resolve()) {
+ ClientCfgCompactEntry *resp_entry = new ClientCfgCompactEntry();
+ assert(resp_entry);
+ resp_entry->m_count = cfg.m_count;
+ resp_entry->fill_from_dir(cfg.m_cfg.m_responder, port + 1);
+ ret.push_back(resp_entry);
+ }
+ }
+ }
+ }
+}
/**
- * loads a YAML file containing
- * the client groups configuration
- *
+ * loads a YAML file containing
+ * the client groups configuration
+ *
*/
void
ClientCfgDB::load_yaml_file(const std::string &filename) {
@@ -93,7 +255,7 @@ ClientCfgDB::load_yaml_file(const std::string &filename) {
/**
* reads a single group of clients from YAML
- *
+ *
*/
void
ClientCfgDB::parse_single_group(YAMLParserWrapper &parser, const YAML::Node &node) {
@@ -116,7 +278,7 @@ ClientCfgDB::parse_single_group(YAMLParserWrapper &parser, const YAML::Node &nod
parse_dir(parser, init, group.m_cfg.m_initiator);
parse_dir(parser, resp, group.m_cfg.m_responder);
-
+
group.m_count = parser.parse_uint(node, "count", 0, UINT64_MAX, 1);
/* add to map with copying */
@@ -124,12 +286,32 @@ ClientCfgDB::parse_single_group(YAMLParserWrapper &parser, const YAML::Node &nod
}
-void
-ClientCfgDB::parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDir &dir) {
+void
+ClientCfgDB::parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDirExt &dir) {
+ if (node.FindValue("src_ip")) {
+ dir.set_src_ip(parser.parse_ip(node, "src_ip"));
+ }
+
+ if (node.FindValue("src_ipv6")) {
+ uint16_t ip_num[8];
+ parser.parse_ipv6(node, "src_ipv6", (unsigned char *)&ip_num);
+ dir.set_src_ipv6(ip_num);
+ }
+
+ if (node.FindValue("next_hop")) {
+ dir.set_next_hop(parser.parse_ip(node, "next_hop"));
+ }
+
+ if (node.FindValue("ipv6_next_hop")) {
+ uint16_t ip_num[8];
+ parser.parse_ipv6(node, "ipv6_next_hop", (unsigned char *)&ip_num);
+ dir.set_ipv6_next_hop(ip_num);
+ }
+
if (node.FindValue("src_mac")) {
dir.set_src_mac_addr(parser.parse_mac_addr(node, "src_mac"));
}
-
+
if (node.FindValue("dst_mac")) {
dir.set_dst_mac_addr(parser.parse_mac_addr(node, "dst_mac"));
}
@@ -141,11 +323,20 @@ ClientCfgDB::parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, Client
parser.parse_err("VLAN config was disabled", node["vlan"]);
}
}
+
+ if ((dir.has_next_hop() || dir.has_ipv6_next_hop()) && (dir.has_dst_mac_addr() || dir.has_src_mac_addr())) {
+ parser.parse_err("Should not configure both next_hop/ipv6_next_hop and dst_mac or src_mac", node);
+ }
+
+ if (dir.has_next_hop() && dir.has_ipv6_next_hop()) {
+ parser.parse_err("Should not configure both next_hop and ipv6_next_hop", node);
+ }
+
}
/**
* sanity checks
- *
+ *
* @author imarom (28-Jun-16)
*/
void
@@ -154,7 +345,7 @@ ClientCfgDB::verify(const YAMLParserWrapper &parser) const {
uint32_t monotonic = 0;
/* check that no interval overlaps */
-
+
/* all intervals do not overloap iff when sorted each start/end dots are strong monotonic */
for (const auto &p : m_groups) {
const ClientCfgEntry &group = p.second;
@@ -169,13 +360,13 @@ ClientCfgDB::verify(const YAMLParserWrapper &parser) const {
}
/**
- * lookup function
- * should be fast
- *
+ * lookup function
+ * should be fast
+ *
*/
ClientCfgEntry *
ClientCfgDB::lookup(uint32_t ip) {
-
+
/* a cache to avoid constant search (usually its a range of IPs) */
if ( (m_cache_group) && (m_cache_group->contains(ip)) ) {
return m_cache_group;
@@ -185,7 +376,7 @@ ClientCfgDB::lookup(uint32_t ip) {
m_cache_group = NULL;
std::map<uint32_t ,ClientCfgEntry>::iterator it;
-
+
/* upper bound fetchs the first greater element */
it = m_groups.upper_bound(ip);
@@ -217,12 +408,12 @@ ClientCfgDB::lookup(uint32_t ip) {
/**
* for convenience - search by IP as string
- *
+ *
* @author imarom (28-Jun-16)
- *
- * @param ip
- *
- * @return ClientCfgEntry*
+ *
+ * @param ip
+ *
+ * @return ClientCfgEntry*
*/
ClientCfgEntry *
ClientCfgDB::lookup(const std::string &ip) {
@@ -231,5 +422,3 @@ ClientCfgDB::lookup(const std::string &ip) {
return lookup(addr);
}
-
-
diff --git a/src/trex_client_config.h b/src/trex_client_config.h
index a5bb83b3..257d354f 100644
--- a/src/trex_client_config.h
+++ b/src/trex_client_config.h
@@ -24,57 +24,56 @@ limitations under the License.
#include <stdint.h>
#include <string>
#include <map>
+#include "utl_ip.h"
+#include "common/Network/Packet/MacAddress.h"
class YAMLParserWrapper;
+struct CTupleGenYamlInfo;
+class ClientCfgDirExt;
-
+// To save memory, we use here the ClientCfgExt and ClientCfgDirExt,
+// and in tuple_gen the ClientCfgBase and ClientCfgDirBase
/**
* client configuration per direction
- *
+ *
* @author imarom (29-Jun-16)
*/
-class ClientCfgDir {
+class ClientCfgDirBase {
+ friend class ClientCfgCompactEntry;
-private:
+ protected:
enum {
- HAS_SRC_MAC = 0x1,
- HAS_DST_MAC = 0x2,
- HAS_VLAN = 0x4,
+ HAS_SRC_MAC = 0x1,
+ HAS_DST_MAC = 0x2,
+ HAS_VLAN = 0x4,
+ HAS_NEXT_HOP = 0x8,
+ HAS_IPV6_NEXT_HOP = 0x10,
+ HAS_SRC_IP = 0x20,
+ HAS_SRC_IPV6 = 0x40,
};
- uint8_t m_src_mac[6];
- uint8_t m_dst_mac[6];
+ MacAddress m_src_mac;
+ MacAddress m_dst_mac;
uint16_t m_vlan;
uint8_t m_bitfield;
-
-public:
- ClientCfgDir() {
+ public:
+ ClientCfgDirBase() {
m_bitfield = 0;
}
+ virtual void dump(FILE *fd) const;
bool has_src_mac_addr() const {
return (m_bitfield & HAS_SRC_MAC);
}
- bool has_dst_mac_addr() const {
- return (m_bitfield & HAS_DST_MAC);
- }
- bool has_vlan() const {
- return (m_bitfield & HAS_VLAN);
- }
-
void set_src_mac_addr(uint64_t mac_addr) {
- for (int i = 0; i < 6; i++) {
- m_src_mac[i] = ( mac_addr >> ((5 - i) * 8) ) & 0xFF;
- }
+ m_src_mac.set(mac_addr);
m_bitfield |= HAS_SRC_MAC;
}
void set_dst_mac_addr(uint64_t mac_addr) {
- for (int i = 0; i < 6; i++) {
- m_dst_mac[i] = ( mac_addr >> ((5 - i) * 8) ) & 0xFF;
- }
+ m_dst_mac.set(mac_addr);
m_bitfield |= HAS_DST_MAC;
}
@@ -83,26 +82,21 @@ public:
m_bitfield |= HAS_VLAN;
}
- /* updates a configuration with a group index member */
-
- void update(uint32_t index) {
- if (has_src_mac_addr()) {
- mac_add(m_src_mac, index);
- }
-
- if (has_dst_mac_addr()) {
- mac_add(m_dst_mac, index);
- }
+ bool has_dst_mac_addr() const {
+ return (m_bitfield & HAS_DST_MAC);
+ }
+ bool has_vlan() const {
+ return (m_bitfield & HAS_VLAN);
}
const uint8_t *get_src_mac_addr() const {
assert(has_src_mac_addr());
- return m_src_mac;
+ return m_src_mac.GetConstBuffer();
}
const uint8_t *get_dst_mac_addr() const {
assert(has_dst_mac_addr());
- return m_dst_mac;
+ return m_dst_mac.GetConstBuffer();
}
uint16_t get_vlan() const {
@@ -110,64 +104,165 @@ public:
return m_vlan;
}
+ /* updates a configuration with a group index member */
+ void update(uint32_t index, const ClientCfgDirExt &cfg);
+};
+
+class ClientCfgDirExt : public ClientCfgDirBase {
+ friend class ClientCfgCompactEntry;
+
private:
- /**
- * transform MAC address to uint64_t
- * performs add and return to MAC format
- *
- */
- void mac_add(uint8_t *mac, uint32_t i) {
- uint64_t tmp = 0;
+ enum {
+ HAS_SRC_MAC = 0x1,
+ HAS_DST_MAC = 0x2,
+ HAS_VLAN = 0x4,
+ HAS_NEXT_HOP = 0x8,
+ HAS_IPV6_NEXT_HOP = 0x10,
+ HAS_SRC_IP = 0x20,
+ HAS_SRC_IPV6 = 0x40,
+ };
- for (int i = 0; i < 6; i++) {
- tmp <<= 8;
- tmp |= mac[i];
+ uint32_t m_next_hop;
+ uint32_t m_src_ip;
+ uint16_t m_src_ipv6[8];
+ uint16_t m_ipv6_next_hop[8];
+ std::vector <MacAddress> m_resolved_macs;
+
+public:
+ void dump(FILE *fd) const;
+ void set_resolved_macs(CManyIPInfo &pretest_result, uint16_t count);
+ bool need_resolve() const;
+ void set_no_resolve_needed();
+
+ bool has_ipv6_next_hop() const {
+ return (m_bitfield & HAS_IPV6_NEXT_HOP);
+ }
+
+ bool has_next_hop() const {
+ return (m_bitfield & HAS_NEXT_HOP);
+ }
+
+ bool has_src_ip() const {
+ return (m_bitfield & HAS_SRC_IP);
+ }
+
+ bool has_src_ipv6() const {
+ return (m_bitfield & HAS_SRC_IPV6);
+ }
+
+ void set_src_ip(uint32_t src_ip) {
+ m_src_ip = src_ip;
+ m_bitfield |= HAS_SRC_IP;
+ }
+
+ void set_src_ipv6(const uint16_t src_ipv6[8]) {
+ for (int i = 0; i < 8; i++) {
+ m_src_ipv6[i] = src_ipv6[i];
}
+ m_bitfield |= HAS_SRC_IPV6;
+ }
- tmp += i;
+ void set_next_hop(uint32_t next_hop) {
+ m_next_hop = next_hop;
+ m_bitfield |= HAS_NEXT_HOP;
+ }
- for (int i = 0; i < 6; i++) {
- mac[i] = ( tmp >> ((5 - i) * 8) ) & 0xFF;
+ void set_ipv6_next_hop(const uint16_t next_hop[8]) {
+ for (int i = 0; i < 8; i++) {
+ m_ipv6_next_hop[i] = next_hop[i];
}
+ m_bitfield |= HAS_IPV6_NEXT_HOP;
+ }
+ virtual MacAddress get_resolved_mac(uint16_t index) const {
+ return m_resolved_macs[index];
}
+
};
+class ClientCfgExt;
+
/**
* single client config
- *
+ *
*/
-class ClientCfg {
+class ClientCfgBase {
public:
+ virtual void dump (FILE *fd) const {
+ fprintf(fd, " initiator :\n");
+ m_initiator.dump(fd);
+ fprintf(fd, " responder :\n");
+ m_responder.dump(fd);
+ }
+ virtual void update(uint32_t index, const ClientCfgExt *cfg);
- void update(uint32_t index) {
- m_initiator.update(index);
- m_responder.update(index);
+ public:
+ ClientCfgDirBase m_initiator;
+ ClientCfgDirBase m_responder;
+};
+
+class ClientCfgExt : public ClientCfgBase {
+public:
+ virtual void dump (FILE *fd) const {
+ fprintf(fd, " initiator:\n");
+ m_initiator.dump(fd);
+ fprintf(fd, " responder:\n");
+ m_responder.dump(fd);
}
- ClientCfgDir m_initiator;
- ClientCfgDir m_responder;
+ ClientCfgDirExt m_initiator;
+ ClientCfgDirExt m_responder;
+};
+
+class ClientCfgCompactEntry {
+ friend class ClientCfgDB;
+ public:
+ uint16_t get_count() {return m_count;}
+ uint16_t get_vlan() {return m_vlan;}
+ uint16_t get_port() {return m_port;}
+ bool is_ipv4() {return m_is_ipv4;}
+ uint32_t get_dst_ip() {return m_next_hop_base.ip;}
+ uint16_t *get_dst_ipv6() {return m_next_hop_base.ipv6;}
+ uint32_t get_src_ip() {return m_src_ip.ip;}
+ uint16_t *get_src_ipv6() {return m_src_ip.ipv6;}
+
+ public:
+ void fill_from_dir(ClientCfgDirExt cfg, uint8_t port_id);
+
+ private:
+ uint16_t m_count;
+ uint16_t m_vlan;
+ uint8_t m_port;
+ bool m_is_ipv4;
+ union {
+ uint32_t ip;
+ uint16_t ipv6[8];
+ } m_next_hop_base;
+ union {
+ uint32_t ip;
+ uint16_t ipv6[8];
+ } m_src_ip;
+
};
/******************************** internal section ********************************/
/**
- * describes a single client config
+ * describes a single client config
* entry loaded from the config file
- *
+ *
*/
class ClientCfgEntry {
-
+ friend class basic_client_cfg_test1_Test;
public:
ClientCfgEntry() {
reset();
}
-
- void dump() const;
-
+ void dump(FILE *fd) const;
+ void set_resolved_macs(CManyIPInfo &pretest_result);
bool contains(uint32_t ip) const {
return ( (ip >= m_ip_start) && (ip <= m_ip_end) );
}
@@ -176,19 +271,19 @@ public:
m_iterator = 0;
}
-
+
/**
- * assings a client config from the group
- * it will advance MAC addresses andf etc.
- *
+ * assings a client config from the group
+ * it will advance MAC addresses andf etc.
+ *
* @author imarom (27-Jun-16)
- *
- * @param info
+ *
+ * @param info
*/
- void assign(ClientCfg &info) {
+ void assign(ClientCfgBase &info) {
info = m_cfg;
- info.update(m_iterator);
-
+ info.update(m_iterator, &m_cfg);
+
/* advance for the next assign */
m_iterator = (m_iterator + 1) % m_count;
}
@@ -197,69 +292,92 @@ public:
uint32_t m_ip_start;
uint32_t m_ip_end;
- ClientCfg m_cfg;
+ ClientCfgExt m_cfg;
uint32_t m_count;
private:
+ void set_params(uint32_t start, uint32_t end, uint32_t count) { // for tests
+ m_ip_start = start;
+ m_ip_end = end;
+ m_count = count;
+ }
+ void set_cfg(const ClientCfgExt &cfg) {
+ m_cfg = cfg;
+ }
uint32_t m_iterator;
};
/**
* holds all the configured clients groups
- *
+ *
*/
class ClientCfgDB {
-public:
+ friend class basic_client_cfg_test1_Test;
+ public:
ClientCfgDB() {
m_is_empty = true;
m_cache_group = NULL;
m_under_vlan = false;
+ m_tg = NULL;
}
+ ~ClientCfgDB() {
+ m_groups.clear();
+ }
+
+ void dump(FILE *fd) ;
+
/**
- * if no config file was loaded
- * this should return true
- *
+ * if no config file was loaded
+ * this should return true
+ *
*/
bool is_empty() {
return m_is_empty;
}
+ void set_resolved_macs(CManyIPInfo &pretest_result);
+ void get_entry_list(std::vector<ClientCfgCompactEntry *> &ret);
+
+
/**
- * loads a YAML file
- * configuration will be built
- * according to the YAML config
- *
+ * loads a YAML file
+ * configuration will be built
+ * according to the YAML config
+ *
*/
void load_yaml_file(const std::string &filename);
/**
- * lookup for a specific IP address for
- * a group that contains this IP
- *
+ * lookup for a specific IP address for
+ * a group that contains this IP
+ *
*/
ClientCfgEntry * lookup(uint32_t ip);
ClientCfgEntry * lookup(const std::string &ip);
+ void set_tuple_gen_info(CTupleGenYamlInfo *tg) {m_tg = tg;}
private:
void parse_single_group(YAMLParserWrapper &parser, const YAML::Node &node);
- void parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDir &dir);
-
+ void parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDirExt &dir);
+ void set_vlan(bool val) {m_under_vlan = val;} // for tests
+ void add_group(uint32_t ip, ClientCfgEntry cfg) { // for tests
+ m_groups.insert(std::make_pair(ip, cfg));
+ }
/**
* verify the YAML file loaded in valid
- *
+ *
*/
void verify(const YAMLParserWrapper &parser) const;
/* maps the IP start value to client groups */
std::map<uint32_t, ClientCfgEntry> m_groups;
- bool m_under_vlan;
-
- ClientCfgEntry *m_cache_group;
- bool m_is_empty;
+ bool m_under_vlan;
+ CTupleGenYamlInfo * m_tg;
+ ClientCfgEntry * m_cache_group;
+ bool m_is_empty;
};
#endif /* __TREX_CLIENT_CONFIG_H__ */
-
diff --git a/src/trex_port_attr.cpp b/src/trex_port_attr.cpp
new file mode 100644
index 00000000..2a68fcb9
--- /dev/null
+++ b/src/trex_port_attr.cpp
@@ -0,0 +1,152 @@
+/*
+Copyright (c) 2015-2015 Cisco Systems, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "trex_port_attr.h"
+#include "bp_sim.h"
+
+DestAttr::DestAttr(uint8_t port_id) {
+ m_port_id = port_id;
+
+ m_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest;
+ m_type = DEST_TYPE_MAC;
+
+ /* save the default */
+ memcpy(m_default_mac, m_mac, 6);
+}
+
+
+/**
+ * set dest as an IPv4 unresolved
+ */
+void
+DestAttr::set_dest(uint32_t ipv4) {
+ assert(ipv4 != 0);
+
+ m_ipv4 = ipv4;
+ memset(m_mac, 0, 6); // just to be on the safe side
+ m_type = DEST_TYPE_IPV4_UNRESOLVED;
+}
+
+/**
+ * set dest as a resolved IPv4
+ */
+void
+DestAttr::set_dest(uint32_t ipv4, const uint8_t *mac) {
+ assert(ipv4 != 0);
+
+ m_ipv4 = ipv4;
+
+ /* source might be the same as dest (this shadows the datapath memory) */
+ memmove(m_mac, mac, 6);
+ m_type = DEST_TYPE_IPV4;
+}
+
+/**
+ * dest dest as MAC
+ *
+ */
+void
+DestAttr::set_dest(const uint8_t *mac) {
+
+ m_ipv4 = 0;
+
+ /* source might be the same as dest (this shadows the datapath memory) */
+ memmove(m_mac, mac, 6);
+ m_type = DEST_TYPE_MAC;
+}
+
+void
+DestAttr::to_json(Json::Value &output) const {
+ switch (m_type) {
+
+ case DEST_TYPE_IPV4:
+ output["type"] = "ipv4";
+ output["ipv4"] = utl_uint32_to_ipv4(m_ipv4);
+ output["arp"] = utl_macaddr_to_str(m_mac);
+ break;
+
+ case DEST_TYPE_IPV4_UNRESOLVED:
+ output["type"] = "ipv4_u";
+ output["ipv4"] = utl_uint32_to_ipv4(m_ipv4);
+ break;
+
+ case DEST_TYPE_MAC:
+ output["type"] = "mac";
+ output["mac"] = utl_macaddr_to_str(m_mac);
+ break;
+
+ default:
+ assert(0);
+ }
+
+}
+
+const uint8_t *
+TRexPortAttr::get_src_mac() const {
+ return CGlobalInfo::m_options.get_src_mac_addr(m_port_id);
+}
+
+void
+TRexPortAttr::set_src_ipv4(uint32_t addr) {
+ m_src_ipv4 = addr;
+
+ /* when IP source changes - consider this as link down */
+ m_dest.on_link_down();
+}
+
+std::string
+TRexPortAttr::get_rx_filter_mode() const {
+ switch (m_rx_filter_mode) {
+ case RX_FILTER_MODE_ALL:
+ return "all";
+ case RX_FILTER_MODE_HW:
+ return "hw";
+ default:
+ assert(0);
+ }
+}
+
+
+void
+TRexPortAttr::to_json(Json::Value &output) {
+
+ output["src_mac"] = utl_macaddr_to_str(get_src_mac());
+ output["promiscuous"]["enabled"] = get_promiscuous();
+ output["link"]["up"] = is_link_up();
+ output["speed"] = get_link_speed() / 1000; // make sure we have no cards of less than 1 Gbps
+ output["rx_filter_mode"] = get_rx_filter_mode();
+
+ if (get_src_ipv4() != 0) {
+ output["src_ipv4"] = utl_uint32_to_ipv4(get_src_ipv4());
+ } else {
+ output["src_ipv4"] = Json::nullValue;
+ }
+
+
+ int mode;
+ get_flow_ctrl(mode);
+ output["fc"]["mode"] = mode;
+
+ m_dest.to_json(output["dest"]);
+
+}
+
+void
+TRexPortAttr::update_src_dst_mac(uint8_t *raw_pkt) const {
+ memcpy(raw_pkt, m_dest.get_dest_mac(), 6);
+ memcpy(raw_pkt + 6, get_src_mac(), 6);
+}
+
diff --git a/src/trex_port_attr.h b/src/trex_port_attr.h
index 9231e263..7336befa 100755
--- a/src/trex_port_attr.h
+++ b/src/trex_port_attr.h
@@ -21,10 +21,93 @@ limitations under the License.
#include <vector>
#include "rte_ethdev_includes.h"
#include "trex_defs.h"
+#include "common/basic_utils.h"
+#include <json/json.h>
+#include "trex_stateless_rx_defs.h"
+#include <string.h>
+
+/**
+ * destination port attribute
+ *
+ */
+class DestAttr {
+
+public:
+
+ DestAttr(uint8_t port_id);
+
+ /**
+ * dest can be either MAC IPv4, or IPv4 unresolved
+ */
+ enum dest_type_e {
+ DEST_TYPE_IPV4 = 1,
+ DEST_TYPE_IPV4_UNRESOLVED,
+ DEST_TYPE_MAC,
+ };
+
+ /**
+ * set dest as an IPv4 unresolved
+ */
+ void set_dest(uint32_t ipv4);
+
+ /**
+ * set dest as a resolved IPv4
+ */
+ void set_dest(uint32_t ipv4, const uint8_t *mac);
+
+ /**
+ * set dest as a plain MAC
+ */
+ void set_dest(const uint8_t *mac);
+
+
+ /**
+ * return true if destination is resolved
+ */
+ bool is_resolved() const {
+ return (m_type != DEST_TYPE_IPV4_UNRESOLVED);
+ }
+
+ /**
+ * get the dest mac
+ * if the dest is not resolved
+ * it will return the default MAC
+ */
+ const uint8_t *get_dest_mac() const {
+ return m_mac;
+ }
+
+ /**
+ * when link gets down - this should be called
+ *
+ */
+ void on_link_down() {
+ if (m_type == DEST_TYPE_IPV4) {
+ /* reset the IPv4 dest with no resolution */
+ set_dest(m_ipv4);
+ }
+ }
+
+ void to_json(Json::Value &output) const;
+
+private:
+ uint32_t m_ipv4;
+ uint8_t *m_mac;
+ dest_type_e m_type;
+ uint8_t m_port_id;
+
+private:
+ uint8_t m_default_mac[6];
+};
class TRexPortAttr {
public:
+
+ TRexPortAttr(uint8_t port_id) : m_dest(port_id) {
+ m_src_ipv4 = 0;
+ }
+
virtual ~TRexPortAttr(){}
/* UPDATES */
@@ -33,10 +116,10 @@ public:
virtual void update_device_info() = 0;
virtual void reset_xstats() = 0;
virtual void update_description() = 0;
-
+
/* GETTERS */
virtual bool get_promiscuous() = 0;
- virtual void macaddr_get(struct ether_addr *mac_addr) = 0;
+ virtual void get_hw_src_mac(struct ether_addr *mac_addr) = 0;
virtual uint32_t get_link_speed() { return m_link.link_speed; } // L1 Mbps
virtual bool is_link_duplex() { return (m_link.link_duplex ? true : false); }
virtual bool is_link_autoneg() { return (m_link.link_autoneg ? true : false); }
@@ -50,25 +133,53 @@ public:
virtual bool is_link_change_supported() { return flag_is_link_change_supported; }
virtual void get_description(std::string &description) { description = intf_info_st.description; }
virtual void get_supported_speeds(supp_speeds_t &supp_speeds) = 0;
+ virtual bool is_loopback() const = 0;
+
+ uint32_t get_src_ipv4() const {return m_src_ipv4;}
+ DestAttr & get_dest() {return m_dest;}
+
+ const uint8_t *get_src_mac() const;
+ std::string get_rx_filter_mode() const;
+ /* for a raw packet, write the src/dst MACs */
+ void update_src_dst_mac(uint8_t *raw_pkt) const;
+
/* SETTERS */
virtual int set_promiscuous(bool enabled) = 0;
virtual int add_mac(char * mac) = 0;
virtual int set_link_up(bool up) = 0;
virtual int set_flow_ctrl(int mode) = 0;
virtual int set_led(bool on) = 0;
-
-/* DUMPS */
+ virtual int set_rx_filter_mode(rx_filter_mode_e mode) = 0;
+
+ void set_src_ipv4(uint32_t addr);
+
+ /* DUMPS */
virtual void dump_link(FILE *fd) = 0;
+ /* dump object status to JSON */
+ void to_json(Json::Value &output);
+
+ uint8_t get_port_id() const {
+ return m_port_id;
+ }
+
protected:
- uint8_t m_port_id;
- rte_eth_link m_link;
- struct rte_eth_dev_info dev_info;
- bool flag_is_virtual;
- bool flag_is_fc_change_supported;
- bool flag_is_led_change_supported;
- bool flag_is_link_change_supported;
+
+ uint8_t m_port_id;
+ rte_eth_link m_link;
+ uint32_t m_src_ipv4;
+ DestAttr m_dest;
+
+ struct rte_eth_dev_info dev_info;
+
+ rx_filter_mode_e m_rx_filter_mode;
+
+ bool flag_is_virtual;
+ bool flag_is_fc_change_supported;
+ bool flag_is_led_change_supported;
+ bool flag_is_link_change_supported;
+
struct intf_info_st {
std::string pci_addr;
@@ -81,8 +192,11 @@ protected:
class DpdkTRexPortAttr : public TRexPortAttr {
public:
- DpdkTRexPortAttr(uint8_t port_id, bool is_virtual, bool fc_change_allowed) {
+ DpdkTRexPortAttr(uint8_t port_id, bool is_virtual, bool fc_change_allowed) : TRexPortAttr(port_id) {
+
m_port_id = port_id;
+ m_rx_filter_mode = RX_FILTER_MODE_HW;
+
flag_is_virtual = is_virtual;
int tmp;
flag_is_fc_change_supported = fc_change_allowed && (get_flow_ctrl(tmp) != -ENOTSUP);
@@ -101,12 +215,13 @@ public:
/* GETTERS */
virtual bool get_promiscuous();
- virtual void macaddr_get(struct ether_addr *mac_addr);
+ virtual void get_hw_src_mac(struct ether_addr *mac_addr);
virtual int get_xstats_values(xstats_values_t &xstats_values);
virtual int get_xstats_names(xstats_names_t &xstats_names);
virtual int get_flow_ctrl(int &mode);
virtual void get_supported_speeds(supp_speeds_t &supp_speeds);
-
+ virtual bool is_loopback() const;
+
/* SETTERS */
virtual int set_promiscuous(bool enabled);
virtual int add_mac(char * mac);
@@ -114,6 +229,7 @@ public:
virtual int set_flow_ctrl(int mode);
virtual int set_led(bool on);
+ virtual int set_rx_filter_mode(rx_filter_mode_e mode);
/* DUMPS */
virtual void dump_link(FILE *fd);
@@ -128,7 +244,7 @@ private:
class SimTRexPortAttr : public TRexPortAttr {
public:
- SimTRexPortAttr() {
+ SimTRexPortAttr() : TRexPortAttr(0) {
m_link.link_speed = 10000;
m_link.link_duplex = 1;
m_link.link_autoneg = 0;
@@ -146,7 +262,7 @@ public:
void reset_xstats() {}
void update_description() {}
bool get_promiscuous() { return false; }
- void macaddr_get(struct ether_addr *mac_addr) {}
+ void get_hw_src_mac(struct ether_addr *mac_addr) {}
int get_xstats_values(xstats_values_t &xstats_values) { return -ENOTSUP; }
int get_xstats_names(xstats_names_t &xstats_names) { return -ENOTSUP; }
int get_flow_ctrl(int &mode) { return -ENOTSUP; }
@@ -158,6 +274,8 @@ public:
int set_flow_ctrl(int mode) { return -ENOTSUP; }
int set_led(bool on) { return -ENOTSUP; }
void dump_link(FILE *fd) {}
+ int set_rx_filter_mode(rx_filter_mode_e mode) { return -ENOTSUP; }
+ virtual bool is_loopback() const { return false; }
};
diff --git a/src/tuple_gen.cpp b/src/tuple_gen.cpp
index 6861b73f..a4509580 100755
--- a/src/tuple_gen.cpp
+++ b/src/tuple_gen.cpp
@@ -6,7 +6,7 @@
*/
/*
-Copyright (c) 2015-2015 Cisco Systems, Inc.
+Copyright (c) 2015-2016 Cisco Systems, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -21,11 +21,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-
-#include "tuple_gen.h"
#include <string.h>
#include "utl_yaml.h"
#include "bp_sim.h"
+#include "tuple_gen.h"
void CServerPool::Create(IP_DIST_t dist_value,
uint32_t min_ip,
@@ -134,7 +133,7 @@ void CClientPool::allocate_configured_clients(uint32_t min_ip,
throw std::runtime_error(ss.str());
}
- ClientCfg info;
+ ClientCfgBase info;
group->assign(info);
if (is_long_range) {
@@ -216,13 +215,84 @@ void CTupleGeneratorSmart::Delete(){
m_server_pool.clear();
}
+void CTupleGenYamlInfo::dump(FILE *fd) {
+ fprintf(fd, "Client pools:\n");
+ for (int i=0; i < m_client_pool.size(); i++) {
+ m_client_pool[i].Dump(fd);
+ }
+ fprintf(fd, "Server pools:\n");
+ for (int i=0; i < m_server_pool.size(); i++) {
+ m_server_pool[i].Dump(fd);
+ }
+}
+
+// Find out matching port for given ip range.
+// If found, port is returned in port, otherwise port is set to UINT8_MAX
+// Return false in case of error. True otherwise. Port not found is not considered error.
+bool CTupleGenYamlInfo::find_port(uint32_t ip_start, uint32_t ip_end, uint8_t &port) {
+ uint8_t num_ports = CGlobalInfo::m_options.get_expected_ports();
+
+ for (int i=0; i < m_client_pool.size(); i++) {
+ CTupleGenPoolYaml &pool = m_client_pool[i];
+ uint32_t pool_start = pool.get_ip_start();
+ uint32_t pool_end = pool.get_ip_end();
+ uint32_t pool_offset = pool.getDualMask();
+ for (uint8_t port_id = 0; port_id < num_ports; port_id += 2) {
+ uint32_t pool_port_start = pool_start + pool_offset * port_id / 2;
+ uint32_t pool_port_end = pool_end + pool_offset * port_id / 2;
+ if ((ip_start >= pool_port_start) && (ip_start <= pool_port_end)) {
+ if ((ip_end >= pool_port_start) && (ip_end <= pool_port_end)) {
+ port = port_id;
+ return true;
+ } else {
+ // ip_start in range, ip_end not
+ fprintf(stderr, "Error for range %s - %s. Start is inside range %s - %s, but end is outside\n"
+ , ip_to_str(ip_start).c_str(), ip_to_str(ip_end).c_str()
+ , ip_to_str(pool_port_start).c_str(), ip_to_str(pool_port_end).c_str());
+ port = UINT8_MAX;
+ return false;
+ }
+ }
+ }
+ }
+
+ for (int i=0; i < m_server_pool.size(); i++) {
+ CTupleGenPoolYaml &pool = m_server_pool[i];
+ uint32_t pool_start = pool.get_ip_start();
+ uint32_t pool_end = pool.get_ip_end();
+ uint32_t pool_offset = pool.getDualMask();
+ for (uint8_t port_id = 1; port_id < num_ports; port_id += 2) {
+ uint32_t pool_port_start = pool_start + pool_offset * (port_id - 1) / 2;
+ uint32_t pool_port_end = pool_end + pool_offset * (port_id - 1)/ 2;
+ if ((ip_start >= pool_port_start) && (ip_start <= pool_port_end)) {
+ if ((ip_end >= pool_port_start) && (ip_end <= pool_port_end)) {
+ port = port_id;
+ return true;
+ } else {
+ fprintf(stderr, "Error for range %s - %s. Start is inside range %s - %s, but end is outside\n"
+ , ip_to_str(ip_start).c_str(), ip_to_str(ip_end).c_str()
+ , ip_to_str(pool_port_start).c_str(), ip_to_str(pool_port_end).c_str());
+ // ip_start in range, ip_end not
+ port = UINT8_MAX;
+ return false;
+ }
+ }
+ }
+ }
+
+ port = UINT8_MAX;
+ return true;
+}
+
void CTupleGenPoolYaml::Dump(FILE *fd){
- fprintf(fd," dist : %d \n",m_dist);
- fprintf(fd," IPs : %08x -%08x \n",m_ip_start,m_ip_end);
- fprintf(fd," clients per gb : %d \n",m_number_of_clients_per_gb);
- fprintf(fd," min clients : %d \n",m_min_clients);
- fprintf(fd," tcp aging : %d sec \n",m_tcp_aging_sec);
- fprintf(fd," udp aging : %d sec \n",m_udp_aging_sec);
+ fprintf(fd," Pool %s:\n", (m_name.size() == 0) ? "default":m_name.c_str());
+ fprintf(fd," dist : %d \n",m_dist);
+ fprintf(fd," IPs : %s - %s \n",ip_to_str(m_ip_start).c_str(), ip_to_str(m_ip_end).c_str());
+ fprintf(fd," dual_port_mask : %s \n",ip_to_str(m_dual_interface_mask).c_str());
+ fprintf(fd," clients per gb : %d \n",m_number_of_clients_per_gb);
+ fprintf(fd," min clients : %d \n",m_min_clients);
+ fprintf(fd," tcp aging : %d sec \n",m_tcp_aging_sec);
+ fprintf(fd," udp aging : %d sec \n",m_udp_aging_sec);
}
bool CTupleGenPoolYaml::is_valid(uint32_t num_threads,bool is_plugins){
@@ -244,10 +314,6 @@ bool CTupleGenPoolYaml::is_valid(uint32_t num_threads,bool is_plugins){
return (true);
}
-
-
-
-
#define UTL_YAML_READ(type, field, target) if (node.FindValue(#field)) { \
utl_yaml_read_ ## type (node, #field , target); \
} else { printf("generator definition mising " #field "\n"); }
diff --git a/src/tuple_gen.h b/src/tuple_gen.h
index 2491f489..e9dc8d4e 100755
--- a/src/tuple_gen.h
+++ b/src/tuple_gen.h
@@ -85,15 +85,15 @@ public:
void setClientPort(uint16_t port) {
m_client_port = port;
}
- void setClientCfg(ClientCfg *cfg) {
+ void setClientCfg(ClientCfgBase *cfg) {
m_client_cfg = cfg;
}
- ClientCfg *getClientCfg() {
+ ClientCfgBase *getClientCfg() {
return m_client_cfg;
}
- void setClientTuple(uint32_t ip, ClientCfg *cfg, uint16_t port) {
+ void setClientTuple(uint32_t ip, ClientCfgBase *cfg, uint16_t port) {
setClient(ip);
setClientPort(port);
setClientCfg(cfg);
@@ -125,7 +125,7 @@ private:
uint32_t m_server_ip;
uint32_t m_server_idx;
- ClientCfg *m_client_cfg;
+ ClientCfgBase *m_client_cfg;
uint16_t m_client_port;
uint16_t m_server_port;
@@ -337,7 +337,7 @@ template <typename T>
class CConfiguredClientInfo : public T {
public:
- CConfiguredClientInfo(uint32_t ip, const ClientCfg &cfg) : m_cfg(cfg) {
+ CConfiguredClientInfo(uint32_t ip, const ClientCfgBase &cfg) : m_cfg(cfg) {
T::set_ip(ip);
}
@@ -348,7 +348,7 @@ public:
}
private:
- ClientCfg m_cfg;
+ ClientCfgBase m_cfg;
};
@@ -852,6 +852,9 @@ struct CTupleGenPoolYaml {
uint32_t get_ip_start() {
return m_ip_start;
}
+ uint32_t get_ip_end() {
+ return m_ip_end;
+ }
bool is_valid(uint32_t num_threads,bool is_plugins);
void Dump(FILE *fd);
};
@@ -888,6 +891,9 @@ public:
exit(-1);
return 0;
}
+
+ bool find_port(uint32_t ip_start, uint32_t ip_end, uint8_t &port);
+ void dump(FILE *fd);
};
diff --git a/src/utl_ip.cpp b/src/utl_ip.cpp
new file mode 100644
index 00000000..d29ab60a
--- /dev/null
+++ b/src/utl_ip.cpp
@@ -0,0 +1,139 @@
+/*
+ Cisco Systems, Inc.
+*/
+
+/*
+Copyright (c) 2016-2016 Cisco Systems, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+#include <string>
+#include <iostream>
+#include <pkt_gen.h>
+#include "utl_ip.h"
+
+void COneIPInfo::dump(FILE *fd, const char *offset) const {
+ uint8_t mac[ETHER_ADDR_LEN];
+ m_mac.copyToArray(mac);
+ char ip_str[100];
+ get_ip_str(ip_str);
+ std::string mac_str;
+ utl_macaddr_to_str(mac, mac_str);
+ const char *mac_char = resolve_needed() ? "Unknown" : mac_str.c_str();
+ fprintf(fd, "%sip: %s ", offset, ip_str);
+ if (m_vlan != 0)
+ fprintf(fd, "vlan: %d ", m_vlan);
+ if (m_port != UINT8_MAX)
+ fprintf(fd, "port: %d ", m_port);
+ fprintf(fd, "mac: %s", mac_char);
+ fprintf(fd, "\n");
+}
+
+bool COneIPInfo::resolve_needed() const {
+ return m_mac.isDefaultAddress();
+}
+
+/*
+ * Fill buffer p with arp request.
+ * port_id - port id we intend to send on
+ * sip - source IP/MAC information
+ */
+void COneIPv4Info::fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip) {
+ uint8_t src_mac[ETHER_ADDR_LEN];
+ sip->get_mac(src_mac);
+
+ CTestPktGen::create_arp_req(p, ((COneIPv4Info *)sip)->get_ip(), m_ip, src_mac, m_vlan, port_id);
+}
+
+void COneIPv4Info::fill_grat_arp_buf(uint8_t *p) {
+ uint8_t src_mac[ETHER_ADDR_LEN];
+ get_mac(src_mac);
+
+ CTestPktGen::create_arp_req(p, m_ip, m_ip, src_mac, m_vlan, 0);
+}
+
+void COneIPv6Info::fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip) {
+ //??? implement ipv6
+}
+
+void COneIPv6Info::fill_grat_arp_buf(uint8_t *p) {
+ //??? implement ipv6
+}
+
+const COneIPInfo *CManyIPInfo::get_next() {
+ const COneIPInfo *ret;
+
+ if (!m_iter_initiated) {
+ m_ipv4_iter = m_ipv4_resolve.begin();
+ m_iter_initiated = true;
+ }
+
+
+ if (m_ipv4_iter == m_ipv4_resolve.end()) {
+ m_ipv4_iter = m_ipv4_resolve.begin();
+ return NULL;
+ }
+
+ ret = &(m_ipv4_iter->second);
+ m_ipv4_iter++;
+ return ret;
+}
+
+void CManyIPInfo::dump(FILE *fd) {
+ ip_vlan_to_many_ip_iter_t it;
+ for (it = m_ipv4_resolve.begin(); it != m_ipv4_resolve.end(); it++) {
+ fprintf(fd, "IPv4 resolved list:\n");
+ uint8_t mac[ETHER_ADDR_LEN];
+ it->second.get_mac(mac);
+ fprintf(fd, "ip:%s vlan: %d resolved to mac %s\n", ip_to_str(it->first.get_ip()).c_str(), it->first.get_vlan()
+ , utl_macaddr_to_str(mac).c_str());
+ }
+}
+
+void CManyIPInfo::insert(const COneIPv4Info &ip_info) {
+ CIpVlan ip_vlan(ip_info.get_ip(), ip_info.get_vlan());
+
+ m_ipv4_resolve.insert(std::make_pair(ip_vlan, ip_info));
+}
+
+bool CManyIPInfo::lookup(uint32_t ip, uint16_t vlan, MacAddress &ret_mac) const {
+ ip_vlan_to_many_ip_iter_t it = m_ipv4_resolve.find(CIpVlan(ip, vlan));
+ if (it != m_ipv4_resolve.end()) {
+ uint8_t mac[ETHER_ADDR_LEN];
+ (*it).second.get_mac(mac);
+ ret_mac.set(mac);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool CManyIPInfo::exists(uint32_t ip, uint16_t vlan) const {
+ ip_vlan_to_many_ip_iter_t it = m_ipv4_resolve.find(CIpVlan(ip, vlan));
+ return (it != m_ipv4_resolve.end());
+}
+
+void CManyIPInfo::clear() {
+ m_ipv4_resolve.clear();
+ m_ipv6_resolve.clear();
+ m_iter_initiated = false;
+}
+
+const COneIPInfo *CManyIPInfo::get_first() {
+ if (m_ipv4_resolve.size() == 0) {
+ return NULL;
+ } else {
+ m_ipv4_iter = m_ipv4_resolve.begin();
+ return &(m_ipv4_iter->second);
+ }
+}
diff --git a/src/utl_ip.h b/src/utl_ip.h
new file mode 100644
index 00000000..bab92c0f
--- /dev/null
+++ b/src/utl_ip.h
@@ -0,0 +1,269 @@
+#ifndef UTL_IP_H
+#define UTL_IP_H
+/*
+ Cisco Systems, Inc.
+*/
+
+/*
+Copyright (c) 2016-2016 Cisco Systems, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+#include <map>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "common/basic_utils.h"
+#include "common/Network/Packet/CPktCmn.h"
+#include "common/Network/Packet/MacAddress.h"
+
+/* IP address, last 32-bits of IPv6 remaps IPv4 */
+typedef struct {
+ uint16_t v6[6]; /* First 96-bits of IPv6 */
+ uint32_t v4; /* Last 32-bits IPv6 overloads v4 */
+} ipaddr_t;
+
+// Routine to create IPv4 address string
+inline int ip_to_str(uint32_t ip,char * str) {
+ uint32_t ipv4 = PKT_HTONL(ip);
+ inet_ntop(AF_INET, (const char *)&ipv4, str, INET_ADDRSTRLEN);
+ return(strlen(str));
+}
+
+inline std::string ip_to_str(uint32_t ip) {
+ char tmp[INET_ADDRSTRLEN];
+ ip_to_str(ip, tmp);
+ return tmp;
+}
+
+// Routine to create IPv6 address string
+inline int ipv6_to_str(ipaddr_t *ip, char * str) {
+ int idx=0;
+ uint16_t ipv6[8];
+ for (uint8_t i=0; i<6; i++) {
+ ipv6[i] = PKT_HTONS(ip->v6[i]);
+ }
+ uint32_t ipv4 = PKT_HTONL(ip->v4);
+ ipv6[6] = ipv4 & 0xffff;
+ ipv6[7] = ipv4 >> 16;
+
+ str[idx++] = '[';
+ inet_ntop(AF_INET6, (const char *)&ipv6, &str[1], INET6_ADDRSTRLEN);
+ idx = strlen(str);
+ str[idx++] = ']';
+ str[idx] = 0;
+ return(idx);
+}
+
+inline std::string ip_to_str(uint8_t *ip) {
+ char tmp[INET6_ADDRSTRLEN];
+ ipv6_to_str((ipaddr_t *)ip, tmp);
+ return tmp;
+}
+
+class CIpVlan {
+ // to be able to use this in map
+ friend bool operator<(const CIpVlan& l, const CIpVlan& r) {
+ if (l.get_ip() == r.get_ip()) {
+ return l.get_vlan() < r.get_vlan();
+ } else {
+ return l.get_ip() < r.get_ip();
+ }
+ }
+
+ public:
+ CIpVlan(uint32_t ip, uint16_t vlan) {
+ m_ip = ip;
+ m_vlan = vlan;
+ }
+ uint16_t get_vlan() const {return m_vlan;}
+ void set_vlan(uint16_t vlan) {m_vlan = vlan;}
+ uint16_t get_ip() const {return m_ip;}
+ void set_ip(uint32_t ip) {m_ip = ip;}
+
+ private:
+ uint32_t m_ip;
+ uint16_t m_vlan;
+};
+
+
+class COneIPInfo {
+ public:
+ enum {
+ IP4_VER=4,
+ IP6_VER=6,
+ } COneIPInfo_ip_types;
+
+ public:
+ virtual ~COneIPInfo() {}
+ virtual void get_mac(uint8_t *mac) const {
+ m_mac.copyToArray(mac);
+ }
+ virtual void set_mac(uint8_t *mac) {
+ m_mac.set(mac);
+ }
+ uint16_t get_vlan() const {return m_vlan;}
+ uint16_t get_port() const {return m_port;}
+ void set_port(uint8_t port) {m_port = port;}
+ virtual void dump(FILE *fd) const {
+ dump(fd, "");
+ }
+ virtual void dump(FILE *fd, const char *offset) const;
+ virtual uint8_t ip_ver() const {return 0;}
+ virtual uint32_t get_arp_req_len() const=0;
+ virtual uint32_t get_grat_arp_len() const=0;
+ virtual void fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip)=0;
+ virtual void fill_grat_arp_buf(uint8_t *p)=0;
+ virtual bool resolve_needed() const;
+
+ protected:
+ COneIPInfo(uint16_t vlan, MacAddress mac, uint8_t port) : m_mac(mac) {
+ m_vlan = vlan;
+ m_port = port;
+ }
+ COneIPInfo(uint16_t vlan, MacAddress mac) : COneIPInfo(vlan, mac, UINT8_MAX) {
+ }
+ virtual const void get_ip_str(char str[100]) const {
+ snprintf(str, 4, "Bad");
+ }
+
+ protected:
+ uint8_t m_port;
+ uint16_t m_vlan;
+ MacAddress m_mac;
+};
+
+class COneIPv4Info : public COneIPInfo {
+ friend bool operator== (const COneIPv4Info& lhs, const COneIPv4Info& rhs);
+
+ public:
+ COneIPv4Info(uint32_t ip, uint16_t vlan, MacAddress mac) : COneIPInfo(vlan, mac) {
+ m_ip = ip;
+ }
+ COneIPv4Info(uint32_t ip, uint16_t vlan) : COneIPv4Info (ip, vlan, MacAddress()) {
+ }
+ COneIPv4Info(uint32_t ip, uint16_t vlan, MacAddress mac, uint8_t port) : COneIPInfo(vlan, mac, port) {
+ m_ip = ip;
+ }
+ ~COneIPv4Info() {};
+ uint32_t get_ip() const {return m_ip;}
+ virtual uint8_t ip_ver() const {return IP4_VER;}
+ virtual uint32_t get_arp_req_len() const {return 60;}
+ virtual uint32_t get_grat_arp_len() const {return 60;}
+ virtual void fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip);
+ virtual void fill_grat_arp_buf(uint8_t *p);
+
+ private:
+ virtual const void get_ip_str(char str[100]) const {
+ ip_to_str(m_ip, str);
+ };
+ uint32_t m_ip;
+};
+
+inline bool operator== (const COneIPv4Info& lhs, const COneIPv4Info& rhs) {
+ if (lhs.m_vlan != rhs.m_vlan)
+ return false;
+
+ if (lhs.m_ip != rhs.m_ip)
+ return false;
+
+ return true;
+}
+
+inline bool operator!= (const COneIPv4Info& lhs, const COneIPv4Info& rhs){ return !(lhs == rhs); }
+
+class COneIPv6Info : public COneIPInfo {
+ friend bool operator== (const COneIPv6Info& lhs, const COneIPv6Info& rhs);
+
+ public:
+ COneIPv6Info(uint16_t ip[8], uint16_t vlan, MacAddress mac) : COneIPInfo(vlan, mac) {
+ memcpy(m_ip, ip, sizeof(m_ip));
+ }
+
+ COneIPv6Info(uint16_t ip[8], uint16_t vlan) : COneIPv6Info(ip, vlan, MacAddress()){
+ }
+
+ COneIPv6Info(uint16_t ip[8], uint16_t vlan, MacAddress mac, uint8_t port) : COneIPInfo(vlan, mac, port) {
+ memcpy(m_ip, ip, sizeof(m_ip));
+ }
+ ~COneIPv6Info() {}
+
+ const uint8_t *get_ipv6() {return (uint8_t *)m_ip;}
+ virtual uint8_t ip_ver() const {return IP6_VER;}
+ virtual uint32_t get_arp_req_len() const {return 100; /* ??? put correct number for ipv6*/}
+ virtual uint32_t get_grat_arp_len() const {return 100; /* ??? put correct number for ipv6*/}
+ virtual void fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip);
+ virtual void fill_grat_arp_buf(uint8_t *p);
+
+ private:
+ virtual const void get_ip_str(char str[100]) {
+ ipv6_to_str((ipaddr_t *)m_ip, str);
+ }
+ uint16_t m_ip[8];
+};
+
+inline bool operator== (const COneIPv6Info& lhs, const COneIPv6Info& rhs) {
+ if (lhs.m_vlan != rhs.m_vlan)
+ return false;
+
+ if (memcmp(&lhs.m_ip, &rhs.m_ip, sizeof(rhs.m_ip)))
+ return false;
+
+ return true;
+}
+
+inline bool operator!= (const COneIPv6Info& lhs, const COneIPv6Info& rhs){ return !(lhs == rhs); }
+
+typedef std::map<CIpVlan, COneIPv4Info> ip_vlan_to_many_ip_t;
+typedef std::map<CIpVlan, COneIPv4Info>::const_iterator ip_vlan_to_many_ip_iter_t;
+typedef std::map<std::pair<uint16_t[8], uint16_t>, COneIPv6Info> ipv6_vlan_to_many_ipv6_t;
+
+class CManyIPInfo {
+ public:
+ CManyIPInfo () {
+ m_iter_initiated = false;
+ }
+ void insert(const COneIPv4Info &ip_info);
+ bool lookup(uint32_t ip, uint16_t vlan, MacAddress &ret_mac) const;
+ bool exists(uint32_t ip, uint16_t vlan = 0) const;
+ void clear();
+
+ void dump(FILE *fd);
+ uint32_t size() { return m_ipv4_resolve.size() + m_ipv6_resolve.size();}
+ const COneIPInfo *get_first();
+ const COneIPInfo *get_next();
+ const COneIPInfo *get_next_loop() {
+ const COneIPInfo *ip_info = get_next();
+ return (ip_info ? ip_info : get_next());
+ }
+
+ CManyIPInfo& operator = (const CManyIPInfo &rhs) {
+ m_ipv4_resolve = rhs.m_ipv4_resolve;
+ m_ipv6_resolve = rhs.m_ipv6_resolve;
+
+ m_iter_initiated = false;
+ return (*this);
+ }
+
+ private:
+ ip_vlan_to_many_ip_t m_ipv4_resolve;
+ ipv6_vlan_to_many_ipv6_t m_ipv6_resolve;
+
+ ip_vlan_to_many_ip_iter_t m_ipv4_iter;
+
+ bool m_iter_initiated;
+
+};
+
+
+#endif
diff --git a/src/utl_term_io.cpp b/src/utl_term_io.cpp
index 8e561188..e45aeebd 100755
--- a/src/utl_term_io.cpp
+++ b/src/utl_term_io.cpp
@@ -78,6 +78,13 @@ int utl_termio_init(){
atexit(exit_handler1);
save_termio();
set_conio_terminal_mode();
+
+ /* stdout is non-blocking */
+ int fd = fileno(stdout);
+ int f = fcntl(fd, F_GETFL, 0);
+ f |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, f);
+
return (0);
}
diff --git a/src/utl_yaml.cpp b/src/utl_yaml.cpp
index 094a3de8..a4fd6404 100755
--- a/src/utl_yaml.cpp
+++ b/src/utl_yaml.cpp
@@ -1,12 +1,10 @@
-#include "utl_yaml.h"
-#include <common/Network/Packet/CPktCmn.h>
/*
Hanoh Haim
Cisco Systems, Inc.
*/
/*
-Copyright (c) 2015-2015 Cisco Systems, Inc.
+Copyright (c) 2015-2016 Cisco Systems, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -24,13 +22,15 @@ limitations under the License.
#include <istream>
#include <fstream>
#include "common/basic_utils.h"
+#include <common/Network/Packet/CPktCmn.h>
+#include "utl_yaml.h"
#define INADDRSZ 4
extern int my_inet_pton4(const char *src, unsigned char *dst);
extern int my_inet_pton6(const char *src, unsigned char *dst);
-bool utl_yaml_read_ip_addr(const YAML::Node& node,
+bool utl_yaml_read_ip_addr(const YAML::Node& node,
const std::string &name,
uint32_t & val){
std::string tmp;
@@ -42,16 +42,14 @@ bool utl_yaml_read_ip_addr(const YAML::Node& node,
val=PKT_NTOHL(ip);
res=true;
}else{
- printf(" ERROR not a valid ip %s \n",(char *)tmp.c_str());
+ printf(" Error: non valid ip %s \n",(char *)tmp.c_str());
exit(-1);
}
}
return (res);
}
-
-
-bool utl_yaml_read_uint32(const YAML::Node& node,
+bool utl_yaml_read_uint32(const YAML::Node& node,
const std::string &name,
uint32_t & val){
bool res=false;
@@ -62,7 +60,7 @@ bool utl_yaml_read_uint32(const YAML::Node& node,
return (res);
}
-bool utl_yaml_read_uint16(const YAML::Node& node,
+bool utl_yaml_read_uint16(const YAML::Node& node,
const std::string &name,
uint16_t & val){
uint32_t val_tmp;
@@ -183,10 +181,10 @@ YAMLParserWrapper::parse_bool(const YAML::Node &node, const std::string &name) {
return (val);
} catch (const YAML::InvalidScalar &ex) {
- parse_err("expecting true/false for field '" + name + "'", node[name]);
+ parse_err("Expecting true/false for field '" + name + "'", node[name]);
} catch (const YAML::KeyNotFound &ex) {
- parse_err("cannot locate mandatory field '" + name + "'", node);
+ parse_err("Can not locate mandatory field '" + name + "'", node);
}
assert(0);
@@ -203,10 +201,10 @@ YAMLParserWrapper::parse_list(const YAML::Node &node, const std::string &name) {
return val;
} catch (const YAML::InvalidScalar &ex) {
- parse_err("expecting sequence/list for field '" + name + "'", node[name]);
+ parse_err("Expecting sequence or list for field '" + name + "'", node[name]);
} catch (const YAML::KeyNotFound &ex) {
- parse_err("cannot locate mandatory field '" + name + "'", node);
+ parse_err("Can not locate mandatory field '" + name + "'", node);
}
assert(0);
@@ -223,10 +221,10 @@ YAMLParserWrapper::parse_map(const YAML::Node &node, const std::string &name) {
return val;
} catch (const YAML::InvalidScalar &ex) {
- parse_err("expecting map for field '" + name + "'", node[name]);
+ parse_err("Expecting map for field '" + name + "'", node[name]);
} catch (const YAML::KeyNotFound &ex) {
- parse_err("cannot locate mandatory field '" + name + "'", node);
+ parse_err("Can not locate mandatory field '" + name + "'", node);
}
assert(0);
@@ -241,16 +239,43 @@ YAMLParserWrapper::parse_ip(const YAML::Node &node, const std::string &name) {
node[name] >> ip_str;
int rc = my_inet_pton4((char *)ip_str.c_str(), (unsigned char *)&ip_num);
if (!rc) {
- parse_err("invalid IP address: " + ip_str, node[name]);
+ parse_err("Invalid IP address: " + ip_str, node[name]);
}
return PKT_NTOHL(ip_num);
} catch (const YAML::InvalidScalar &ex) {
- parse_err("expecting valid IP address for field '" + name + "'", node[name]);
+ parse_err("Expecting valid IP address for field '" + name + "'", node[name]);
+
+ } catch (const YAML::KeyNotFound &ex) {
+ parse_err("Can not locate mandatory field '" + name + "'", node);
+ }
+
+ assert(0);
+}
+
+void
+YAMLParserWrapper::parse_ipv6(const YAML::Node &node, const std::string &name, unsigned char *ip_num) {
+ try {
+ std::string ip_str;
+
+ node[name] >> ip_str;
+ int rc = my_inet_pton6((char *)ip_str.c_str(), ip_num);
+ if (!rc) {
+ parse_err("Invalid IPv6 address: " + ip_str, node[name]);
+ }
+
+ // we want host order
+ for (int i = 0; i < 8; i++) {
+ ((uint16_t *) ip_num)[i] = PKT_NTOHS(((uint16_t *) ip_num)[i]);
+ }
+ return;
+
+ } catch (const YAML::InvalidScalar &ex) {
+ parse_err("Expecting valid IPv6 address for field '" + name + "'", node[name]);
} catch (const YAML::KeyNotFound &ex) {
- parse_err("cannot locate mandatory field '" + name + "'", node);
+ parse_err("Can not locate mandatory field '" + name + "'", node);
}
assert(0);
@@ -276,21 +301,21 @@ YAMLParserWrapper::parse_mac_addr(const YAML::Node &node, const std::string &nam
node[name] >> mac_str;
bool rc = mac2uint64(mac_str, mac_num);
if (!rc) {
- parse_err("invalid MAC address: " + mac_str, node[name]);
+ parse_err("Invalid MAC address: " + mac_str, node[name]);
}
return mac_num;
} catch (const YAML::InvalidScalar &ex) {
- parse_err("expecting true/false for field '" + name + "'", node[name]);
+ parse_err("Expecting true/false for field '" + name + "'", node[name]);
} catch (const YAML::KeyNotFound &ex) {
- parse_err("cannot locate mandatory field '" + name + "'", node);
+ parse_err("Can not locate mandatory field '" + name + "'", node);
}
assert(0);
}
-uint64_t
+uint64_t
YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, uint64_t low, uint64_t high, uint64_t def) {
if (!node.FindValue(name)) {
return def;
@@ -299,7 +324,7 @@ YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, u
return parse_uint(node, name, low, high);
}
-uint64_t
+uint64_t
YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, uint64_t low, uint64_t high) {
try {
@@ -316,10 +341,10 @@ YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, u
return (val);
} catch (const YAML::InvalidScalar &ex) {
- parse_err("expecting true/false for field '" + name + "'", node[name]);
+ parse_err("Expecting true/false for field '" + name + "'", node[name]);
} catch (const YAML::KeyNotFound &ex) {
- parse_err("cannot locate mandatory field '" + name + "'", node);
+ parse_err("Can not locate mandatory field '" + name + "'", node);
}
assert(0);
diff --git a/src/utl_yaml.h b/src/utl_yaml.h
index ed7d66d6..004e82db 100755
--- a/src/utl_yaml.h
+++ b/src/utl_yaml.h
@@ -63,6 +63,7 @@ public:
const YAML::Node & parse_map(const YAML::Node &node, const std::string &name);
uint32_t parse_ip(const YAML::Node &node, const std::string &name);
+ void parse_ipv6(const YAML::Node &node, const std::string &name, unsigned char *ip);
uint64_t parse_mac_addr(const YAML::Node &node, const std::string &name);
uint64_t parse_mac_addr(const YAML::Node &node, const std::string &name, uint64_t def);