Index: stable/6/contrib/file/Magdir/cafebabe =================================================================== --- stable/6/contrib/file/Magdir/cafebabe (nonexistent) +++ stable/6/contrib/file/Magdir/cafebabe (revision 170009) @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------------ +# Cafe Babes unite! +# +# Since Java bytecode and Mach-O fat-files have the same magic number, the test +# must be performed in the same "magic" sequence to get both right. The long +# at offset 4 in a mach-O fat file tells the number of architectures; the short at +# offset 4 in a Java bytecode file is the JVM minor version and the +# short at offset 6 is the JVM major version. Since there are only +# only 18 labeled Mach-O architectures at current, and the first released +# Java class format was version 43.0, we can safely choose any number +# between 18 and 39 to test the number of architectures against +# (and use as a hack). Let's not use 18, because the Mach-O people +# might add another one or two as time goes by... +# +0 belong 0xcafebabe +>4 belong >30 compiled Java class data, +>>6 beshort x version %d. +>>4 beshort x \b%d +>4 belong 1 Mach-O fat file with 1 architecture +>4 belong >1 +>>4 belong <20 Mach-O fat file with %ld architectures Property changes on: stable/6/contrib/file/Magdir/cafebabe ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/contrib/file/Magdir/os400 =================================================================== --- stable/6/contrib/file/Magdir/os400 (nonexistent) +++ stable/6/contrib/file/Magdir/os400 (revision 170009) @@ -0,0 +1,37 @@ +#------------------------------------------------------------------------------ +# os400: file(1) magic for IBM OS/400 files +# +# IBM OS/400 (i5/OS) Save file (SAVF) - gerardo.cacciari@gmail.com +# In spite of its quite variable format (due to internal memory page +# length differences between CISC and RISC versions of the OS) the +# SAVF structure hasn't suitable offsets to identify the catalog +# header in the first descriptor where there are some useful infos, +# so we must search in a somewhat large area for a particular string +# that represents the EBCDIC encoding of 'QSRDSSPC' (save/restore +# descriptor space) preceded by a two byte constant. +# +1090 search/7393 \x19\xDB\xD8\xE2\xD9\xC4\xE2\xE2\xD7\xC3 IBM OS/400 save file data +>&212 byte 0x01 \b, created with SAVOBJ +>&212 byte 0x02 \b, created with SAVLIB +>&212 byte 0x07 \b, created with SAVCFG +>&212 byte 0x08 \b, created with SAVSECDTA +>&212 byte 0x0A \b, created with SAVSECDTA +>&212 byte 0x0B \b, created with SAVDLO +>&212 byte 0x0D \b, created with SAVLICPGM +>&212 byte 0x11 \b, created with SAVCHGOBJ +>&213 byte 0x44 \b, at least V5R4 to open +>&213 byte 0x43 \b, at least V5R3 to open +>&213 byte 0x42 \b, at least V5R2 to open +>&213 byte 0x41 \b, at least V5R1 to open +>&213 byte 0x40 \b, at least V4R5 to open +>&213 byte 0x3F \b, at least V4R4 to open +>&213 byte 0x3E \b, at least V4R3 to open +>&213 byte 0x3C \b, at least V4R2 to open +>&213 byte 0x3D \b, at least V4R1M4 to open +>&213 byte 0x3B \b, at least V4R1 to open +>&213 byte 0x3A \b, at least V3R7 to open +>&213 byte 0x35 \b, at least V3R6 to open +>&213 byte 0x36 \b, at least V3R2 to open +>&213 byte 0x34 \b, at least V3R1 to open +>&213 byte 0x31 \b, at least V3R0M5 to open +>&213 byte 0x30 \b, at least V2R3 to open Property changes on: stable/6/contrib/file/Magdir/os400 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/contrib/file/Magdir/unicode =================================================================== --- stable/6/contrib/file/Magdir/unicode (nonexistent) +++ stable/6/contrib/file/Magdir/unicode (revision 170009) @@ -0,0 +1,15 @@ + +#--------------------------------------------------------------------------- +# Unicode: BOM prefixed text files - Adrian Havill +# +0 string +/v8 Unicode text, UTF-7 +0 string +/v9 Unicode text, UTF-7 +0 string +/v+ Unicode text, UTF-7 +0 string +/v/ Unicode text, UTF-7 +0 string \357\273\277 Unicode text, UTF-8 +0 string \335\163\146\163 Unicode text, UTF-8-EBCDIC +0 string \376\377\000\000 Unicode text, UTF-32, big-endian +0 string \377\376\000\000 Unicode text, UTF-32, little-endian +0 string \376\377 Unicode text, UTF-16, big-endian +0 string \377\376 Unicode text, UTF-16, little-endian +0 string \016\376\377 Unicode text, SCSU (Standard Compression Scheme for Unicode) Property changes on: stable/6/contrib/file/Magdir/unicode ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/common/cxgb_ctl_defs.h =================================================================== --- stable/6/sys/dev/cxgb/common/cxgb_ctl_defs.h (nonexistent) +++ stable/6/sys/dev/cxgb/common/cxgb_ctl_defs.h (revision 170009) @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2003-2007 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + * + * $FreeBSD$ + */ + +#ifndef _CXGB3_OFFLOAD_CTL_DEFS_H +#define _CXGB3_OFFLOAD_CTL_DEFS_H + + +enum { + GET_MAX_OUTSTANDING_WR, + GET_TX_MAX_CHUNK, + GET_TID_RANGE, + GET_STID_RANGE, + GET_RTBL_RANGE, + GET_L2T_CAPACITY, + GET_MTUS, + GET_WR_LEN, + GET_IFF_FROM_MAC, + GET_DDP_PARAMS, + GET_PORTS, + FAILOVER, + FAILOVER_DONE, + FAILOVER_CLEAR, + + ULP_ISCSI_GET_PARAMS, + ULP_ISCSI_SET_PARAMS, + + RDMA_GET_PARAMS, + RDMA_CQ_OP, + RDMA_CQ_SETUP, + RDMA_CQ_DISABLE, + RDMA_CTRL_QP_SETUP, + RDMA_GET_MEM, +}; + +/* + * Structure used to describe a TID range. Valid TIDs are [base, base+num). + */ +struct tid_range { + unsigned int base; /* first TID */ + unsigned int num; /* number of TIDs in range */ +}; + +/* + * Structure used to request the size and contents of the MTU table. + */ +struct mtutab { + unsigned int size; /* # of entries in the MTU table */ + const unsigned short *mtus; /* the MTU table values */ +}; + +struct net_device; + +/* + * Structure used to request the adapter net_device owning a given MAC address. + */ +struct iff_mac { + struct net_device *dev; /* the net_device */ + const unsigned char *mac_addr; /* MAC address to lookup */ + u16 vlan_tag; +}; + +struct pci_dev; + +/* + * Structure used to request the TCP DDP parameters. + */ +struct ddp_params { + unsigned int llimit; /* TDDP region start address */ + unsigned int ulimit; /* TDDP region end address */ + unsigned int tag_mask; /* TDDP tag mask */ + struct pci_dev *pdev; +}; + +struct adap_ports { + unsigned int nports; /* number of ports on this adapter */ + struct ifnet *lldevs[4]; +}; + +/* + * Structure used to return information to the iscsi layer. + */ +struct ulp_iscsi_info { + unsigned int offset; + unsigned int llimit; + unsigned int ulimit; + unsigned int tagmask; + unsigned int pgsz3; + unsigned int pgsz2; + unsigned int pgsz1; + unsigned int pgsz0; + unsigned int max_rxsz; + unsigned int max_txsz; + struct pci_dev *pdev; +}; + +/* + * Structure used to return information to the RDMA layer. + */ +struct rdma_info { + unsigned int tpt_base; /* TPT base address */ + unsigned int tpt_top; /* TPT last entry address */ + unsigned int pbl_base; /* PBL base address */ + unsigned int pbl_top; /* PBL last entry address */ + unsigned int rqt_base; /* RQT base address */ + unsigned int rqt_top; /* RQT last entry address */ + unsigned int udbell_len; /* user doorbell region length */ + unsigned long udbell_physbase; /* user doorbell physical start addr */ + void volatile *kdb_addr; /* kernel doorbell register address */ + struct pci_dev *pdev; /* associated PCI device */ +}; + +/* + * Structure used to request an operation on an RDMA completion queue. + */ +struct rdma_cq_op { + unsigned int id; + unsigned int op; + unsigned int credits; +}; + +/* + * Structure used to setup RDMA completion queues. + */ +struct rdma_cq_setup { + unsigned int id; + unsigned long long base_addr; + unsigned int size; + unsigned int credits; + unsigned int credit_thres; + unsigned int ovfl_mode; +}; + +/* + * Structure used to setup the RDMA control egress context. + */ +struct rdma_ctrlqp_setup { + unsigned long long base_addr; + unsigned int size; +}; +#endif /* _CXGB3_OFFLOAD_CTL_DEFS_H */ Property changes on: stable/6/sys/dev/cxgb/common/cxgb_ctl_defs.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/common/jhash.h =================================================================== --- stable/6/sys/dev/cxgb/common/jhash.h (nonexistent) +++ stable/6/sys/dev/cxgb/common/jhash.h (revision 170009) @@ -0,0 +1,140 @@ +#ifndef _JHASH_H +#define _JHASH_H + +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup2.c, by Bob Jenkins, December 1996, Public Domain. + * hash(), hash2(), hash3, and mix() are externally useful functions. + * Routines to test the hash are included if SELF_TEST is defined. + * You can use this free for any purpose. It has no warranty. + * + * $FreeBSD$ + */ + +/* NOTE: Arguments are modified. */ +#define __jhash_mix(a, b, c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* The golden ration: an arbitrary value */ +#define JHASH_GOLDEN_RATIO 0x9e3779b9 + +/* The most generic version, hashes an arbitrary sequence + * of bytes. No alignment or length assumptions are made about + * the input key. + */ +static inline u32 jhash(const void *key, u32 length, u32 initval) +{ + u32 a, b, c, len; + const u8 *k = key; + + len = length; + a = b = JHASH_GOLDEN_RATIO; + c = initval; + + while (len >= 12) { + a += (k[0] +((u32)k[1]<<8) +((u32)k[2]<<16) +((u32)k[3]<<24)); + b += (k[4] +((u32)k[5]<<8) +((u32)k[6]<<16) +((u32)k[7]<<24)); + c += (k[8] +((u32)k[9]<<8) +((u32)k[10]<<16)+((u32)k[11]<<24)); + + __jhash_mix(a,b,c); + + k += 12; + len -= 12; + } + + c += length; + switch (len) { + case 11: c += ((u32)k[10]<<24); + case 10: c += ((u32)k[9]<<16); + case 9 : c += ((u32)k[8]<<8); + case 8 : b += ((u32)k[7]<<24); + case 7 : b += ((u32)k[6]<<16); + case 6 : b += ((u32)k[5]<<8); + case 5 : b += k[4]; + case 4 : a += ((u32)k[3]<<24); + case 3 : a += ((u32)k[2]<<16); + case 2 : a += ((u32)k[1]<<8); + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + +/* A special optimized version that handles 1 or more of u32s. + * The length parameter here is the number of u32s in the key. + */ +static inline u32 jhash2(u32 *k, u32 length, u32 initval) +{ + u32 a, b, c, len; + + a = b = JHASH_GOLDEN_RATIO; + c = initval; + len = length; + + while (len >= 3) { + a += k[0]; + b += k[1]; + c += k[2]; + __jhash_mix(a, b, c); + k += 3; len -= 3; + } + + c += length * 4; + + switch (len) { + case 2 : b += k[1]; + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + + +/* A special ultra-optimized versions that knows they are hashing exactly + * 3, 2 or 1 word(s). + * + * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally + * done at the end is not done here. + */ +static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) +{ + a += JHASH_GOLDEN_RATIO; + b += JHASH_GOLDEN_RATIO; + c += initval; + + __jhash_mix(a, b, c); + + return c; +} + +static inline u32 jhash_2words(u32 a, u32 b, u32 initval) +{ + return jhash_3words(a, b, 0, initval); +} + +static inline u32 jhash_1word(u32 a, u32 initval) +{ + return jhash_3words(a, 0, 0, initval); +} + +#endif /* _JHASH_H */ Property changes on: stable/6/sys/dev/cxgb/common/jhash.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/cxgb_l2t.c =================================================================== --- stable/6/sys/dev/cxgb/cxgb_l2t.c (nonexistent) +++ stable/6/sys/dev/cxgb/cxgb_l2t.c (revision 170009) @@ -0,0 +1,670 @@ +/************************************************************************** + +Copyright (c) 2007, Chelsio Inc. +All rights reserved. + +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. Neither the name of the Chelsio Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +***************************************************************************/ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include + +#define VLAN_NONE 0xfff +#define SDL(s) ((struct sockaddr_dl *)s) +#define RT_ENADDR(rt) ((char *)LLADDR(SDL((rt)))) +#define rt_expire rt_rmx.rmx_expire + +struct llinfo_arp { + struct callout la_timer; + struct rtentry *la_rt; + struct mbuf *la_hold; /* last packet until resolved/timeout */ + u_short la_preempt; /* countdown for pre-expiry arps */ + u_short la_asked; /* # requests sent */ +}; + +/* + * Module locking notes: There is a RW lock protecting the L2 table as a + * whole plus a spinlock per L2T entry. Entry lookups and allocations happen + * under the protection of the table lock, individual entry changes happen + * while holding that entry's spinlock. The table lock nests outside the + * entry locks. Allocations of new entries take the table lock as writers so + * no other lookups can happen while allocating new entries. Entry updates + * take the table lock as readers so multiple entries can be updated in + * parallel. An L2T entry can be dropped by decrementing its reference count + * and therefore can happen in parallel with entry allocation but no entry + * can change state or increment its ref count during allocation as both of + * these perform lookups. + */ + +static inline unsigned int +vlan_prio(const struct l2t_entry *e) +{ + return e->vlan >> 13; +} + +static inline unsigned int +arp_hash(u32 key, int ifindex, const struct l2t_data *d) +{ + return jhash_2words(key, ifindex, 0) & (d->nentries - 1); +} + +static inline void +neigh_replace(struct l2t_entry *e, struct rtentry *rt) +{ + RT_LOCK(rt); + RT_ADDREF(rt); + RT_UNLOCK(rt); + + if (e->neigh) { + RT_LOCK(e->neigh); + RT_REMREF(e->neigh); + RT_UNLOCK(e->neigh); + } + e->neigh = rt; +} + +/* + * Set up an L2T entry and send any packets waiting in the arp queue. The + * supplied mbuf is used for the CPL_L2T_WRITE_REQ. Must be called with the + * entry locked. + */ +static int +setup_l2e_send_pending(struct toedev *dev, struct mbuf *m, + struct l2t_entry *e) +{ + struct cpl_l2t_write_req *req; + + if (!m) { + if ((m = m_gethdr(M_NOWAIT, MT_DATA)) == NULL) + return (ENOMEM); + } + /* + * XXX MH_ALIGN + */ + req = mtod(m, struct cpl_l2t_write_req *); + req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); + OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, e->idx)); + req->params = htonl(V_L2T_W_IDX(e->idx) | V_L2T_W_IFF(e->smt_idx) | + V_L2T_W_VLAN(e->vlan & EVL_VLID_MASK) | + V_L2T_W_PRIO(vlan_prio(e))); + + memcpy(e->dmac, RT_ENADDR(e->neigh), sizeof(e->dmac)); + memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac)); + m_set_priority(m, CPL_PRIORITY_CONTROL); + cxgb_ofld_send(dev, m); + while (e->arpq_head) { + m = e->arpq_head; + e->arpq_head = m->m_next; + m->m_next = NULL; + cxgb_ofld_send(dev, m); + } + e->arpq_tail = NULL; + e->state = L2T_STATE_VALID; + + return 0; +} + +/* + * Add a packet to the an L2T entry's queue of packets awaiting resolution. + * Must be called with the entry's lock held. + */ +static inline void +arpq_enqueue(struct l2t_entry *e, struct mbuf *m) +{ + m->m_next = NULL; + if (e->arpq_head) + e->arpq_tail->m_next = m; + else + e->arpq_head = m; + e->arpq_tail = m; +} + +int +t3_l2t_send_slow(struct toedev *dev, struct mbuf *m, + struct l2t_entry *e) +{ + struct rtentry *rt; + struct mbuf *m0; + + if ((m0 = m_gethdr(M_NOWAIT, MT_DATA)) == NULL) + return (ENOMEM); + + rt = e->neigh; + +again: + switch (e->state) { + case L2T_STATE_STALE: /* entry is stale, kick off revalidation */ + arpresolve(rt->rt_ifp, rt, m0, rt->rt_gateway, RT_ENADDR(rt)); + mtx_lock(&e->lock); + if (e->state == L2T_STATE_STALE) + e->state = L2T_STATE_VALID; + mtx_unlock(&e->lock); + case L2T_STATE_VALID: /* fast-path, send the packet on */ + return cxgb_ofld_send(dev, m); + case L2T_STATE_RESOLVING: + mtx_lock(&e->lock); + if (e->state != L2T_STATE_RESOLVING) { // ARP already completed + mtx_unlock(&e->lock); + goto again; + } + arpq_enqueue(e, m); + mtx_unlock(&e->lock); + + if ((m0 = m_gethdr(M_NOWAIT, MT_DATA)) == NULL) + return (ENOMEM); + /* + * Only the first packet added to the arpq should kick off + * resolution. However, because the m_gethdr below can fail, + * we allow each packet added to the arpq to retry resolution + * as a way of recovering from transient memory exhaustion. + * A better way would be to use a work request to retry L2T + * entries when there's no memory. + */ + if (arpresolve(rt->rt_ifp, rt, m0, rt->rt_gateway, RT_ENADDR(rt)) == 0) { + + mtx_lock(&e->lock); + if (e->arpq_head) + setup_l2e_send_pending(dev, m, e); + else + m_freem(m); + mtx_unlock(&e->lock); + } + } + return 0; +} + +void +t3_l2t_send_event(struct toedev *dev, struct l2t_entry *e) +{ + struct rtentry *rt; + struct mbuf *m0; + + if ((m0 = m_gethdr(M_NOWAIT, MT_DATA)) == NULL) + return; + + rt = e->neigh; +again: + switch (e->state) { + case L2T_STATE_STALE: /* entry is stale, kick off revalidation */ + arpresolve(rt->rt_ifp, rt, m0, rt->rt_gateway, RT_ENADDR(rt)); + mtx_lock(&e->lock); + if (e->state == L2T_STATE_STALE) { + e->state = L2T_STATE_VALID; + } + mtx_unlock(&e->lock); + return; + case L2T_STATE_VALID: /* fast-path, send the packet on */ + return; + case L2T_STATE_RESOLVING: + mtx_lock(&e->lock); + if (e->state != L2T_STATE_RESOLVING) { // ARP already completed + mtx_unlock(&e->lock); + goto again; + } + mtx_unlock(&e->lock); + + if ((m0 = m_gethdr(M_NOWAIT, MT_DATA)) == NULL) + return; + /* + * Only the first packet added to the arpq should kick off + * resolution. However, because the alloc_skb below can fail, + * we allow each packet added to the arpq to retry resolution + * as a way of recovering from transient memory exhaustion. + * A better way would be to use a work request to retry L2T + * entries when there's no memory. + */ + arpresolve(rt->rt_ifp, rt, m0, rt->rt_gateway, RT_ENADDR(rt)); + + } + return; +} +/* + * Allocate a free L2T entry. Must be called with l2t_data.lock held. + */ +static struct l2t_entry * +alloc_l2e(struct l2t_data *d) +{ + struct l2t_entry *end, *e, **p; + + if (!atomic_load_acq_int(&d->nfree)) + return NULL; + + /* there's definitely a free entry */ + for (e = d->rover, end = &d->l2tab[d->nentries]; e != end; ++e) + if (atomic_load_acq_int(&e->refcnt) == 0) + goto found; + + for (e = &d->l2tab[1]; atomic_load_acq_int(&e->refcnt); ++e) ; +found: + d->rover = e + 1; + atomic_add_int(&d->nfree, -1); + + /* + * The entry we found may be an inactive entry that is + * presently in the hash table. We need to remove it. + */ + if (e->state != L2T_STATE_UNUSED) { + int hash = arp_hash(e->addr, e->ifindex, d); + + for (p = &d->l2tab[hash].first; *p; p = &(*p)->next) + if (*p == e) { + *p = e->next; + break; + } + e->state = L2T_STATE_UNUSED; + } + return e; +} + +/* + * Called when an L2T entry has no more users. The entry is left in the hash + * table since it is likely to be reused but we also bump nfree to indicate + * that the entry can be reallocated for a different neighbor. We also drop + * the existing neighbor reference in case the neighbor is going away and is + * waiting on our reference. + * + * Because entries can be reallocated to other neighbors once their ref count + * drops to 0 we need to take the entry's lock to avoid races with a new + * incarnation. + */ +void +t3_l2e_free(struct l2t_data *d, struct l2t_entry *e) +{ + mtx_lock(&e->lock); + if (atomic_load_acq_int(&e->refcnt) == 0) { /* hasn't been recycled */ + if (e->neigh) { + RT_LOCK(e->neigh); + RT_REMREF(e->neigh); + RT_UNLOCK(e->neigh); + e->neigh = NULL; + } + } + mtx_unlock(&e->lock); + atomic_add_int(&d->nfree, 1); +} + +/* + * Update an L2T entry that was previously used for the same next hop as neigh. + * Must be called with softirqs disabled. + */ +static inline void +reuse_entry(struct l2t_entry *e, struct rtentry *neigh) +{ + struct llinfo_arp *la; + + la = (struct llinfo_arp *)neigh->rt_llinfo; + + mtx_lock(&e->lock); /* avoid race with t3_l2t_free */ + if (neigh != e->neigh) + neigh_replace(e, neigh); + + if (memcmp(e->dmac, RT_ENADDR(neigh), sizeof(e->dmac)) || + (neigh->rt_expire > time_uptime)) + e->state = L2T_STATE_RESOLVING; + else if (la->la_hold == NULL) + e->state = L2T_STATE_VALID; + else + e->state = L2T_STATE_STALE; + mtx_unlock(&e->lock); +} + +struct l2t_entry * +t3_l2t_get(struct toedev *dev, struct rtentry *neigh, + unsigned int smt_idx) +{ + struct l2t_entry *e; + struct l2t_data *d = L2DATA(dev); + u32 addr = *(u32 *) rt_key(neigh); + int ifidx = neigh->rt_ifp->if_index; + int hash = arp_hash(addr, ifidx, d); + + rw_wlock(&d->lock); + for (e = d->l2tab[hash].first; e; e = e->next) + if (e->addr == addr && e->ifindex == ifidx && + e->smt_idx == smt_idx) { + l2t_hold(d, e); + if (atomic_load_acq_int(&e->refcnt) == 1) + reuse_entry(e, neigh); + goto done; + } + + /* Need to allocate a new entry */ + e = alloc_l2e(d); + if (e) { + mtx_lock(&e->lock); /* avoid race with t3_l2t_free */ + e->next = d->l2tab[hash].first; + d->l2tab[hash].first = e; + e->state = L2T_STATE_RESOLVING; + e->addr = addr; + e->ifindex = ifidx; + e->smt_idx = smt_idx; + atomic_store_rel_int(&e->refcnt, 1); + neigh_replace(e, neigh); +#ifdef notyet + /* + * XXX need to add accessor function for vlan tag + */ + if (neigh->rt_ifp->if_vlantrunk) + e->vlan = VLAN_DEV_INFO(neigh->dev)->vlan_id; + else +#endif + e->vlan = VLAN_NONE; + mtx_unlock(&e->lock); + } +done: + rw_wunlock(&d->lock); + return e; +} + +/* + * Called when address resolution fails for an L2T entry to handle packets + * on the arpq head. If a packet specifies a failure handler it is invoked, + * otherwise the packets is sent to the TOE. + * + * XXX: maybe we should abandon the latter behavior and just require a failure + * handler. + */ +static void +handle_failed_resolution(struct toedev *dev, struct mbuf *arpq) +{ + + while (arpq) { + struct mbuf *m = arpq; +#ifdef notyet + struct l2t_mbuf_cb *cb = L2T_MBUF_CB(m); +#endif + arpq = m->m_next; + m->m_next = NULL; +#ifdef notyet + if (cb->arp_failure_handler) + cb->arp_failure_handler(dev, m); + else +#endif + cxgb_ofld_send(dev, m); + } + +} + +#if defined(NETEVENT) || !defined(CONFIG_CHELSIO_T3_MODULE) +/* + * Called when the host's ARP layer makes a change to some entry that is + * loaded into the HW L2 table. + */ +void +t3_l2t_update(struct toedev *dev, struct rtentry *neigh) +{ + struct l2t_entry *e; + struct mbuf *arpq = NULL; + struct l2t_data *d = L2DATA(dev); + u32 addr = *(u32 *) rt_key(neigh); + int ifidx = neigh->rt_ifp->if_index; + int hash = arp_hash(addr, ifidx, d); + struct llinfo_arp *la; + + rw_rlock(&d->lock); + for (e = d->l2tab[hash].first; e; e = e->next) + if (e->addr == addr && e->ifindex == ifidx) { + mtx_lock(&e->lock); + goto found; + } + rw_runlock(&d->lock); + return; + +found: + rw_runlock(&d->lock); + if (atomic_load_acq_int(&e->refcnt)) { + if (neigh != e->neigh) + neigh_replace(e, neigh); + + la = (struct llinfo_arp *)neigh->rt_llinfo; + if (e->state == L2T_STATE_RESOLVING) { + + if (la->la_asked >= 5 /* arp_maxtries */) { + arpq = e->arpq_head; + e->arpq_head = e->arpq_tail = NULL; + } else if (la->la_hold == NULL) + setup_l2e_send_pending(dev, NULL, e); + } else { + e->state = (la->la_hold == NULL) ? + L2T_STATE_VALID : L2T_STATE_STALE; + if (memcmp(e->dmac, RT_ENADDR(neigh), 6)) + setup_l2e_send_pending(dev, NULL, e); + } + } + mtx_unlock(&e->lock); + + if (arpq) + handle_failed_resolution(dev, arpq); +} +#else +/* + * Called from a kprobe, interrupts are off. + */ +void +t3_l2t_update(struct toedev *dev, struct rtentry *neigh) +{ + struct l2t_entry *e; + struct l2t_data *d = L2DATA(dev); + u32 addr = *(u32 *) rt_key(neigh); + int ifidx = neigh->dev->ifindex; + int hash = arp_hash(addr, ifidx, d); + + rw_rlock(&d->lock); + for (e = d->l2tab[hash].first; e; e = e->next) + if (e->addr == addr && e->ifindex == ifidx) { + mtx_lock(&e->lock); + if (atomic_load_acq_int(&e->refcnt)) { + if (neigh != e->neigh) + neigh_replace(e, neigh); + e->tdev = dev; + mod_timer(&e->update_timer, jiffies + 1); + } + mtx_unlock(&e->lock); + break; + } + rw_runlock(&d->lock); +} + +static void +update_timer_cb(unsigned long data) +{ + struct mbuf *arpq = NULL; + struct l2t_entry *e = (struct l2t_entry *)data; + struct rtentry *neigh = e->neigh; + struct toedev *dev = e->tdev; + + barrier(); + if (!atomic_load_acq_int(&e->refcnt)) + return; + + rw_rlock(&neigh->lock); + mtx_lock(&e->lock); + + if (atomic_load_acq_int(&e->refcnt)) { + if (e->state == L2T_STATE_RESOLVING) { + if (neigh->nud_state & NUD_FAILED) { + arpq = e->arpq_head; + e->arpq_head = e->arpq_tail = NULL; + } else if (neigh_is_connected(neigh) && e->arpq_head) + setup_l2e_send_pending(dev, NULL, e); + } else { + e->state = neigh_is_connected(neigh) ? + L2T_STATE_VALID : L2T_STATE_STALE; + if (memcmp(e->dmac, RT_ENADDR(neigh), sizeof(e->dmac))) + setup_l2e_send_pending(dev, NULL, e); + } + } + mtx_unlock(&e->lock); + rw_runlock(&neigh->lock); + + if (arpq) + handle_failed_resolution(dev, arpq); +} +#endif + +struct l2t_data * +t3_init_l2t(unsigned int l2t_capacity) +{ + struct l2t_data *d; + int i, size = sizeof(*d) + l2t_capacity * sizeof(struct l2t_entry); + + d = cxgb_alloc_mem(size); + if (!d) + return NULL; + + d->nentries = l2t_capacity; + d->rover = &d->l2tab[1]; /* entry 0 is not used */ + atomic_store_rel_int(&d->nfree, l2t_capacity - 1); + rw_init(&d->lock, "L2T"); + + for (i = 0; i < l2t_capacity; ++i) { + d->l2tab[i].idx = i; + d->l2tab[i].state = L2T_STATE_UNUSED; + mtx_init(&d->l2tab[i].lock, "L2TAB", NULL, MTX_DEF); + atomic_store_rel_int(&d->l2tab[i].refcnt, 0); +#ifndef NETEVENT +#ifdef CONFIG_CHELSIO_T3_MODULE + setup_timer(&d->l2tab[i].update_timer, update_timer_cb, + (unsigned long)&d->l2tab[i]); +#endif +#endif + } + return d; +} + +void +t3_free_l2t(struct l2t_data *d) +{ +#ifndef NETEVENT +#ifdef CONFIG_CHELSIO_T3_MODULE + int i; + + /* Stop all L2T timers */ + for (i = 0; i < d->nentries; ++i) + del_timer_sync(&d->l2tab[i].update_timer); +#endif +#endif + cxgb_free_mem(d); +} + +#ifdef CONFIG_PROC_FS +#include +#include +#include + +static inline void * +l2t_get_idx(struct seq_file *seq, loff_t pos) +{ + struct l2t_data *d = seq->private; + + return pos >= d->nentries ? NULL : &d->l2tab[pos]; +} + +static void * +l2t_seq_start(struct seq_file *seq, loff_t *pos) +{ + return *pos ? l2t_get_idx(seq, *pos) : SEQ_START_TOKEN; +} + +static void * +l2t_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + v = l2t_get_idx(seq, *pos + 1); + if (v) + ++*pos; + return v; +} + +static void +l2t_seq_stop(struct seq_file *seq, void *v) +{ +} + +static char +l2e_state(const struct l2t_entry *e) +{ + switch (e->state) { + case L2T_STATE_VALID: return 'V'; /* valid, fast-path entry */ + case L2T_STATE_STALE: return 'S'; /* needs revalidation, but usable */ + case L2T_STATE_RESOLVING: + return e->arpq_head ? 'A' : 'R'; + default: + return 'U'; + } +} + +static int +l2t_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_puts(seq, "Index IP address Ethernet address VLAN " + "Prio State Users SMTIDX Port\n"); + else { + char ip[20]; + struct l2t_entry *e = v; + + mtx_lock(&e->lock); + sprintf(ip, "%u.%u.%u.%u", NIPQUAD(e->addr)); + seq_printf(seq, "%-5u %-15s %02x:%02x:%02x:%02x:%02x:%02x %4d" + " %3u %c %7u %4u %s\n", + e->idx, ip, e->dmac[0], e->dmac[1], e->dmac[2], + e->dmac[3], e->dmac[4], e->dmac[5], + e->vlan & EVL_VLID_MASK, vlan_prio(e), + l2e_state(e), atomic_load_acq_int(&e->refcnt), e->smt_idx, + e->neigh ? e->neigh->dev->name : ""); + mtx_unlock(&e->lock); + } + return 0; +} + +#endif Property changes on: stable/6/sys/dev/cxgb/cxgb_l2t.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/cxgb_l2t.h =================================================================== --- stable/6/sys/dev/cxgb/cxgb_l2t.h (nonexistent) +++ stable/6/sys/dev/cxgb/cxgb_l2t.h (revision 170009) @@ -0,0 +1,154 @@ +/************************************************************************** + +Copyright (c) 2007, Chelsio Inc. +All rights reserved. + +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. Neither the name of the Chelsio Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$FreeBSD$ + +***************************************************************************/ +#ifndef _CHELSIO_L2T_H +#define _CHELSIO_L2T_H + +#include +#include +#include + +enum { + L2T_STATE_VALID, /* entry is up to date */ + L2T_STATE_STALE, /* entry may be used but needs revalidation */ + L2T_STATE_RESOLVING, /* entry needs address resolution */ + L2T_STATE_UNUSED /* entry not in use */ +}; + +/* + * Each L2T entry plays multiple roles. First of all, it keeps state for the + * corresponding entry of the HW L2 table and maintains a queue of offload + * packets awaiting address resolution. Second, it is a node of a hash table + * chain, where the nodes of the chain are linked together through their next + * pointer. Finally, each node is a bucket of a hash table, pointing to the + * first element in its chain through its first pointer. + */ +struct l2t_entry { + uint16_t state; /* entry state */ + uint16_t idx; /* entry index */ + uint32_t addr; /* dest IP address */ + int ifindex; /* neighbor's net_device's ifindex */ + uint16_t smt_idx; /* SMT index */ + uint16_t vlan; /* VLAN TCI (id: bits 0-11, prio: 13-15 */ + struct rtentry *neigh; /* associated neighbour */ + struct l2t_entry *first; /* start of hash chain */ + struct l2t_entry *next; /* next l2t_entry on chain */ + struct mbuf *arpq_head; /* queue of packets awaiting resolution */ + struct mbuf *arpq_tail; + struct mtx lock; + volatile uint32_t refcnt; /* entry reference count */ + uint8_t dmac[6]; /* neighbour's MAC address */ +#ifndef NETEVENT +#ifdef CONFIG_CHELSIO_T3_MODULE + struct timer_list update_timer; + struct toedev *tdev; +#endif +#endif +}; + +struct l2t_data { + unsigned int nentries; /* number of entries */ + struct l2t_entry *rover; /* starting point for next allocation */ + volatile uint32_t nfree; /* number of free entries */ + struct rwlock lock; + struct l2t_entry l2tab[0]; +}; + +typedef void (*arp_failure_handler_func)(struct toedev *dev, + struct mbuf *m); + +/* + * Callback stored in an skb to handle address resolution failure. + */ +struct l2t_mbuf_cb { + arp_failure_handler_func arp_failure_handler; +}; + +/* + * XXX + */ +#define L2T_MBUF_CB(skb) ((struct l2t_mbuf_cb *)(skb)->cb) + + +static __inline void set_arp_failure_handler(struct mbuf *m, + arp_failure_handler_func hnd) +{ +#if 0 + L2T_SKB_CB(skb)->arp_failure_handler = hnd; +#endif + panic("implement me"); +} + +/* + * Getting to the L2 data from an offload device. + */ +#define L2DATA(dev) ((dev)->l2opt) + +void t3_l2e_free(struct l2t_data *d, struct l2t_entry *e); +void t3_l2t_update(struct toedev *dev, struct rtentry *ifp); +struct l2t_entry *t3_l2t_get(struct toedev *dev, struct rtentry *neigh, + unsigned int smt_idx); +int t3_l2t_send_slow(struct toedev *dev, struct mbuf *m, + struct l2t_entry *e); +void t3_l2t_send_event(struct toedev *dev, struct l2t_entry *e); +struct l2t_data *t3_init_l2t(unsigned int l2t_capacity); +void t3_free_l2t(struct l2t_data *d); + +#ifdef CONFIG_PROC_FS +int t3_l2t_proc_setup(struct proc_dir_entry *dir, struct l2t_data *d); +void t3_l2t_proc_free(struct proc_dir_entry *dir); +#else +#define l2t_proc_setup(dir, d) 0 +#define l2t_proc_free(dir) +#endif + +int cxgb_ofld_send(struct toedev *dev, struct mbuf *m); + +static inline int l2t_send(struct toedev *dev, struct mbuf *m, + struct l2t_entry *e) +{ + if (__predict_true(e->state == L2T_STATE_VALID)) + return cxgb_ofld_send(dev, m); + return t3_l2t_send_slow(dev, m, e); +} + +static inline void l2t_release(struct l2t_data *d, struct l2t_entry *e) +{ + if (atomic_fetchadd_int(&e->refcnt, -1) == 1) + t3_l2e_free(d, e); +} + +static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e) +{ + if (atomic_fetchadd_int(&e->refcnt, 1) == 1) /* 0 -> 1 transition */ + atomic_add_int(&d->nfree, 1); +} + +#endif Property changes on: stable/6/sys/dev/cxgb/cxgb_l2t.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/cxgb_offload.c =================================================================== --- stable/6/sys/dev/cxgb/cxgb_offload.c (nonexistent) +++ stable/6/sys/dev/cxgb/cxgb_offload.c (revision 170009) @@ -0,0 +1,1633 @@ + +/************************************************************************** + +Copyright (c) 2007, Chelsio Inc. +All rights reserved. + +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. Neither the name of the Chelsio Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + + +***************************************************************************/ + + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * XXX + */ +#define LOG_NOTICE 2 +#define BUG_ON(...) +#define VALIDATE_TID 0 + + +TAILQ_HEAD(, cxgb_client) client_list; +TAILQ_HEAD(, toedev) ofld_dev_list; +TAILQ_HEAD(, adapter) adapter_list; + +static struct mtx cxgb_db_lock; +static struct rwlock adapter_list_lock; + + +static const unsigned int MAX_ATIDS = 64 * 1024; +static const unsigned int ATID_BASE = 0x100000; +static int inited = 0; + +static inline int +offload_activated(struct toedev *tdev) +{ + struct adapter *adapter = tdev2adap(tdev); + + return (isset(&adapter->open_device_map, OFFLOAD_DEVMAP_BIT)); +} + +/** + * cxgb_register_client - register an offload client + * @client: the client + * + * Add the client to the client list, + * and call backs the client for each activated offload device + */ +void +cxgb_register_client(struct cxgb_client *client) +{ + struct toedev *tdev; + + mtx_lock(&cxgb_db_lock); + TAILQ_INSERT_TAIL(&client_list, client, client_entry); + + if (client->add) { + TAILQ_FOREACH(tdev, &ofld_dev_list, ofld_entry) { + if (offload_activated(tdev)) + client->add(tdev); + } + } + mtx_unlock(&cxgb_db_lock); +} + +/** + * cxgb_unregister_client - unregister an offload client + * @client: the client + * + * Remove the client to the client list, + * and call backs the client for each activated offload device. + */ +void +cxgb_unregister_client(struct cxgb_client *client) +{ + struct toedev *tdev; + + mtx_lock(&cxgb_db_lock); + TAILQ_REMOVE(&client_list, client, client_entry); + + if (client->remove) { + TAILQ_FOREACH(tdev, &ofld_dev_list, ofld_entry) { + if (offload_activated(tdev)) + client->remove(tdev); + } + } + mtx_unlock(&cxgb_db_lock); +} + +/** + * cxgb_add_clients - activate register clients for an offload device + * @tdev: the offload device + * + * Call backs all registered clients once a offload device is activated + */ +void +cxgb_add_clients(struct toedev *tdev) +{ + struct cxgb_client *client; + + mtx_lock(&cxgb_db_lock); + TAILQ_FOREACH(client, &client_list, client_entry) { + if (client->add) + client->add(tdev); + } + mtx_unlock(&cxgb_db_lock); +} + +/** + * cxgb_remove_clients - activate register clients for an offload device + * @tdev: the offload device + * + * Call backs all registered clients once a offload device is deactivated + */ +void +cxgb_remove_clients(struct toedev *tdev) +{ + struct cxgb_client *client; + + mtx_lock(&cxgb_db_lock); + TAILQ_FOREACH(client, &client_list, client_entry) { + if (client->remove) + client->remove(tdev); + } + mtx_unlock(&cxgb_db_lock); +} + +static int +is_offloading(struct ifnet *ifp) +{ + struct adapter *adapter; + int port; + + rw_rlock(&adapter_list_lock); + TAILQ_FOREACH(adapter, &adapter_list, adapter_entry) { + for_each_port(adapter, port) { + if (ifp == adapter->port[port].ifp) { + rw_runlock(&adapter_list_lock); + return 1; + } + } + } + rw_runlock(&adapter_list_lock); + return 0; +} + +static struct ifnet * +get_iff_from_mac(adapter_t *adapter, const uint8_t *mac, unsigned int vlan) +{ +#ifdef notyet + int i; + + for_each_port(adapter, i) { + const struct vlan_group *grp; + const struct port_info *p = &adapter->port[i]; + struct ifnet *ifnet = p->ifp; + + if (!memcmp(p->hw_addr, mac, ETHER_ADDR_LEN)) { + if (vlan && vlan != EVL_VLID_MASK) { + grp = p->vlan_grp; + dev = grp ? grp->vlan_devices[vlan] : NULL; + } else + while (dev->master) + dev = dev->master; + return dev; + } + } +#endif + return NULL; +} + +static inline void +failover_fixup(adapter_t *adapter, int port) +{ + if (adapter->params.rev == 0) { + struct ifnet *ifp = adapter->port[port].ifp; + struct cmac *mac = &adapter->port[port].mac; + if (!(ifp->if_flags & IFF_UP)) { + /* Failover triggered by the interface ifdown */ + t3_write_reg(adapter, A_XGM_TX_CTRL + mac->offset, + F_TXEN); + t3_read_reg(adapter, A_XGM_TX_CTRL + mac->offset); + } else { + /* Failover triggered by the interface link down */ + t3_write_reg(adapter, A_XGM_RX_CTRL + mac->offset, 0); + t3_read_reg(adapter, A_XGM_RX_CTRL + mac->offset); + t3_write_reg(adapter, A_XGM_RX_CTRL + mac->offset, + F_RXEN); + } + } +} + +static int +cxgb_ulp_iscsi_ctl(adapter_t *adapter, unsigned int req, void *data) +{ + int ret = 0; + struct ulp_iscsi_info *uiip = data; + + switch (req) { + case ULP_ISCSI_GET_PARAMS: + uiip->llimit = t3_read_reg(adapter, A_ULPRX_ISCSI_LLIMIT); + uiip->ulimit = t3_read_reg(adapter, A_ULPRX_ISCSI_ULIMIT); + uiip->tagmask = t3_read_reg(adapter, A_ULPRX_ISCSI_TAGMASK); + /* + * On tx, the iscsi pdu has to be <= tx page size and has to + * fit into the Tx PM FIFO. + */ + uiip->max_txsz = min(adapter->params.tp.tx_pg_size, + t3_read_reg(adapter, A_PM1_TX_CFG) >> 17); + /* on rx, the iscsi pdu has to be < rx page size and the + whole pdu + cpl headers has to fit into one sge buffer */ + uiip->max_rxsz = + (unsigned int)min(adapter->params.tp.rx_pg_size, + (adapter->sge.qs[0].fl[1].buf_size - + sizeof(struct cpl_rx_data) * 2 - + sizeof(struct cpl_rx_data_ddp)) ); + break; + case ULP_ISCSI_SET_PARAMS: + t3_write_reg(adapter, A_ULPRX_ISCSI_TAGMASK, uiip->tagmask); + break; + default: + ret = -EOPNOTSUPP; + } + return ret; +} + +/* Response queue used for RDMA events. */ +#define ASYNC_NOTIF_RSPQ 0 + +static int +cxgb_rdma_ctl(adapter_t *adapter, unsigned int req, void *data) +{ + int ret = 0; + + switch (req) { + case RDMA_GET_PARAMS: { + struct rdma_info *req = data; + + req->udbell_physbase = rman_get_start(adapter->regs_res); + req->udbell_len = rman_get_size(adapter->regs_res); + req->tpt_base = t3_read_reg(adapter, A_ULPTX_TPT_LLIMIT); + req->tpt_top = t3_read_reg(adapter, A_ULPTX_TPT_ULIMIT); + req->pbl_base = t3_read_reg(adapter, A_ULPTX_PBL_LLIMIT); + req->pbl_top = t3_read_reg(adapter, A_ULPTX_PBL_ULIMIT); + req->rqt_base = t3_read_reg(adapter, A_ULPRX_RQ_LLIMIT); + req->rqt_top = t3_read_reg(adapter, A_ULPRX_RQ_ULIMIT); + req->kdb_addr = (void *)(rman_get_start(adapter->regs_res) + A_SG_KDOORBELL); + break; + } + case RDMA_CQ_OP: { + struct rdma_cq_op *req = data; + + /* may be called in any context */ + mtx_lock(&adapter->sge.reg_lock); + ret = t3_sge_cqcntxt_op(adapter, req->id, req->op, + req->credits); + mtx_unlock(&adapter->sge.reg_lock); + break; + } + case RDMA_GET_MEM: { + struct ch_mem_range *t = data; + struct mc7 *mem; + + if ((t->addr & 7) || (t->len & 7)) + return -EINVAL; + if (t->mem_id == MEM_CM) + mem = &adapter->cm; + else if (t->mem_id == MEM_PMRX) + mem = &adapter->pmrx; + else if (t->mem_id == MEM_PMTX) + mem = &adapter->pmtx; + else + return -EINVAL; + + ret = t3_mc7_bd_read(mem, t->addr/8, t->len/8, (u64 *)t->buf); + if (ret) + return ret; + break; + } + case RDMA_CQ_SETUP: { + struct rdma_cq_setup *req = data; + + mtx_lock(&adapter->sge.reg_lock); + ret = t3_sge_init_cqcntxt(adapter, req->id, req->base_addr, + req->size, ASYNC_NOTIF_RSPQ, + req->ovfl_mode, req->credits, + req->credit_thres); + mtx_unlock(&adapter->sge.reg_lock); + break; + } + case RDMA_CQ_DISABLE: + mtx_lock(&adapter->sge.reg_lock); + ret = t3_sge_disable_cqcntxt(adapter, *(unsigned int *)data); + mtx_unlock(&adapter->sge.reg_lock); + break; + case RDMA_CTRL_QP_SETUP: { + struct rdma_ctrlqp_setup *req = data; + + mtx_lock(&adapter->sge.reg_lock); + ret = t3_sge_init_ecntxt(adapter, FW_RI_SGEEC_START, 0, + SGE_CNTXT_RDMA, ASYNC_NOTIF_RSPQ, + req->base_addr, req->size, + FW_RI_TID_START, 1, 0); + mtx_unlock(&adapter->sge.reg_lock); + break; + } + default: + ret = -EOPNOTSUPP; + } + return ret; +} + +static int +cxgb_offload_ctl(struct toedev *tdev, unsigned int req, void *data) +{ + struct adapter *adapter = tdev2adap(tdev); + struct tid_range *tid; + struct mtutab *mtup; + struct iff_mac *iffmacp; + struct ddp_params *ddpp; + struct adap_ports *ports; + int port; + + switch (req) { + case GET_MAX_OUTSTANDING_WR: + *(unsigned int *)data = FW_WR_NUM; + break; + case GET_WR_LEN: + *(unsigned int *)data = WR_FLITS; + break; + case GET_TX_MAX_CHUNK: + *(unsigned int *)data = 1 << 20; /* 1MB */ + break; + case GET_TID_RANGE: + tid = data; + tid->num = t3_mc5_size(&adapter->mc5) - + adapter->params.mc5.nroutes - + adapter->params.mc5.nfilters - + adapter->params.mc5.nservers; + tid->base = 0; + break; + case GET_STID_RANGE: + tid = data; + tid->num = adapter->params.mc5.nservers; + tid->base = t3_mc5_size(&adapter->mc5) - tid->num - + adapter->params.mc5.nfilters - + adapter->params.mc5.nroutes; + break; + case GET_L2T_CAPACITY: + *(unsigned int *)data = 2048; + break; + case GET_MTUS: + mtup = data; + mtup->size = NMTUS; + mtup->mtus = adapter->params.mtus; + break; + case GET_IFF_FROM_MAC: + iffmacp = data; + iffmacp->dev = get_iff_from_mac(adapter, iffmacp->mac_addr, + iffmacp->vlan_tag & EVL_VLID_MASK); + break; + case GET_DDP_PARAMS: + ddpp = data; + ddpp->llimit = t3_read_reg(adapter, A_ULPRX_TDDP_LLIMIT); + ddpp->ulimit = t3_read_reg(adapter, A_ULPRX_TDDP_ULIMIT); + ddpp->tag_mask = t3_read_reg(adapter, A_ULPRX_TDDP_TAGMASK); + break; + case GET_PORTS: + ports = data; + ports->nports = adapter->params.nports; + for_each_port(adapter, port) + ports->lldevs[port] = adapter->port[port].ifp; + break; + case FAILOVER: + port = *(int *)data; + t3_port_failover(adapter, port); + failover_fixup(adapter, port); + break; + case FAILOVER_DONE: + port = *(int *)data; + t3_failover_done(adapter, port); + break; + case FAILOVER_CLEAR: + t3_failover_clear(adapter); + break; + case ULP_ISCSI_GET_PARAMS: + case ULP_ISCSI_SET_PARAMS: + if (!offload_running(adapter)) + return -EAGAIN; + return cxgb_ulp_iscsi_ctl(adapter, req, data); + case RDMA_GET_PARAMS: + case RDMA_CQ_OP: + case RDMA_CQ_SETUP: + case RDMA_CQ_DISABLE: + case RDMA_CTRL_QP_SETUP: + case RDMA_GET_MEM: + if (!offload_running(adapter)) + return -EAGAIN; + return cxgb_rdma_ctl(adapter, req, data); + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * Dummy handler for Rx offload packets in case we get an offload packet before + * proper processing is setup. This complains and drops the packet as it isn't + * normal to get offload packets at this stage. + */ +static int +rx_offload_blackhole(struct toedev *dev, struct mbuf **m, int n) +{ + CH_ERR(tdev2adap(dev), "%d unexpected offload packets, first data %u\n", + n, ntohl(*mtod(m[0], uint32_t *))); + while (n--) + m_freem(m[n]); + return 0; +} + +static void +dummy_neigh_update(struct toedev *dev, struct rtentry *neigh) +{ +} + +void +cxgb_set_dummy_ops(struct toedev *dev) +{ + dev->recv = rx_offload_blackhole; + dev->neigh_update = dummy_neigh_update; +} + +/* + * Free an active-open TID. + */ +void * +cxgb_free_atid(struct toedev *tdev, int atid) +{ + struct tid_info *t = &(TOE_DATA(tdev))->tid_maps; + union active_open_entry *p = atid2entry(t, atid); + void *ctx = p->toe_tid.ctx; + + mtx_lock(&t->atid_lock); + p->next = t->afree; + t->afree = p; + t->atids_in_use--; + mtx_lock(&t->atid_lock); + + return ctx; +} + +/* + * Free a server TID and return it to the free pool. + */ +void +cxgb_free_stid(struct toedev *tdev, int stid) +{ + struct tid_info *t = &(TOE_DATA(tdev))->tid_maps; + union listen_entry *p = stid2entry(t, stid); + + mtx_lock(&t->stid_lock); + p->next = t->sfree; + t->sfree = p; + t->stids_in_use--; + mtx_unlock(&t->stid_lock); +} + +void +cxgb_insert_tid(struct toedev *tdev, struct cxgb_client *client, + void *ctx, unsigned int tid) +{ + struct tid_info *t = &(TOE_DATA(tdev))->tid_maps; + + t->tid_tab[tid].client = client; + t->tid_tab[tid].ctx = ctx; + atomic_add_int(&t->tids_in_use, 1); +} + +/* + * Populate a TID_RELEASE WR. The mbuf must be already propely sized. + */ +static inline void +mk_tid_release(struct mbuf *m, unsigned int tid) +{ + struct cpl_tid_release *req; + + m_set_priority(m, CPL_PRIORITY_SETUP); + req = mtod(m, struct cpl_tid_release *); + req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); + OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_TID_RELEASE, tid)); +} + +static void +t3_process_tid_release_list(void *data, int pending) +{ + struct mbuf *m; + struct toedev *tdev = data; + struct toe_data *td = TOE_DATA(tdev); + + mtx_lock(&td->tid_release_lock); + while (td->tid_release_list) { + struct toe_tid_entry *p = td->tid_release_list; + + td->tid_release_list = (struct toe_tid_entry *)p->ctx; + mtx_unlock(&td->tid_release_lock); + m = m_get(M_WAIT, MT_DATA); + mk_tid_release(m, p - td->tid_maps.tid_tab); + cxgb_ofld_send(tdev, m); + p->ctx = NULL; + mtx_lock(&td->tid_release_lock); + } + mtx_unlock(&td->tid_release_lock); +} + +/* use ctx as a next pointer in the tid release list */ +void +cxgb_queue_tid_release(struct toedev *tdev, unsigned int tid) +{ + struct toe_data *td = TOE_DATA(tdev); + struct toe_tid_entry *p = &td->tid_maps.tid_tab[tid]; + + mtx_lock(&td->tid_release_lock); + p->ctx = td->tid_release_list; + td->tid_release_list = p; + + if (!p->ctx) + taskqueue_enqueue(tdev->adapter->tq, &td->tid_release_task); + + mtx_unlock(&td->tid_release_lock); +} + +/* + * Remove a tid from the TID table. A client may defer processing its last + * CPL message if it is locked at the time it arrives, and while the message + * sits in the client's backlog the TID may be reused for another connection. + * To handle this we atomically switch the TID association if it still points + * to the original client context. + */ +void +cxgb_remove_tid(struct toedev *tdev, void *ctx, unsigned int tid) +{ + struct tid_info *t = &(TOE_DATA(tdev))->tid_maps; + + BUG_ON(tid >= t->ntids); + if (tdev->type == T3A) + atomic_cmpset_ptr((long *)&t->tid_tab[tid].ctx, (long)NULL, (long)ctx); + else { + struct mbuf *m; + + m = m_get(M_NOWAIT, MT_DATA); + if (__predict_true(m != NULL)) { + mk_tid_release(m, tid); + cxgb_ofld_send(tdev, m); + t->tid_tab[tid].ctx = NULL; + } else + cxgb_queue_tid_release(tdev, tid); + } + atomic_add_int(&t->tids_in_use, -1); +} + +int +cxgb_alloc_atid(struct toedev *tdev, struct cxgb_client *client, + void *ctx) +{ + int atid = -1; + struct tid_info *t = &(TOE_DATA(tdev))->tid_maps; + + mtx_lock(&t->atid_lock); + if (t->afree) { + union active_open_entry *p = t->afree; + + atid = (p - t->atid_tab) + t->atid_base; + t->afree = p->next; + p->toe_tid.ctx = ctx; + p->toe_tid.client = client; + t->atids_in_use++; + } + mtx_unlock(&t->atid_lock); + return atid; +} + +int +cxgb_alloc_stid(struct toedev *tdev, struct cxgb_client *client, + void *ctx) +{ + int stid = -1; + struct tid_info *t = &(TOE_DATA(tdev))->tid_maps; + + mtx_lock(&t->stid_lock); + if (t->sfree) { + union listen_entry *p = t->sfree; + + stid = (p - t->stid_tab) + t->stid_base; + t->sfree = p->next; + p->toe_tid.ctx = ctx; + p->toe_tid.client = client; + t->stids_in_use++; + } + mtx_unlock(&t->stid_lock); + return stid; +} + +static int +do_smt_write_rpl(struct toedev *dev, struct mbuf *m) +{ + struct cpl_smt_write_rpl *rpl = cplhdr(m); + + if (rpl->status != CPL_ERR_NONE) + log(LOG_ERR, + "Unexpected SMT_WRITE_RPL status %u for entry %u\n", + rpl->status, GET_TID(rpl)); + + return CPL_RET_BUF_DONE; +} + +static int +do_l2t_write_rpl(struct toedev *dev, struct mbuf *m) +{ + struct cpl_l2t_write_rpl *rpl = cplhdr(m); + + if (rpl->status != CPL_ERR_NONE) + log(LOG_ERR, + "Unexpected L2T_WRITE_RPL status %u for entry %u\n", + rpl->status, GET_TID(rpl)); + + return CPL_RET_BUF_DONE; +} + +static int +do_act_open_rpl(struct toedev *dev, struct mbuf *m) +{ + struct cpl_act_open_rpl *rpl = cplhdr(m); + unsigned int atid = G_TID(ntohl(rpl->atid)); + struct toe_tid_entry *toe_tid; + + toe_tid = lookup_atid(&(TOE_DATA(dev))->tid_maps, atid); + if (toe_tid->ctx && toe_tid->client && toe_tid->client->handlers && + toe_tid->client->handlers[CPL_ACT_OPEN_RPL]) { + return toe_tid->client->handlers[CPL_ACT_OPEN_RPL] (dev, m, + toe_tid->ctx); + } else { + log(LOG_ERR, "%s: received clientless CPL command 0x%x\n", + dev->name, CPL_ACT_OPEN_RPL); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int +do_stid_rpl(struct toedev *dev, struct mbuf *m) +{ + union opcode_tid *p = cplhdr(m); + unsigned int stid = G_TID(ntohl(p->opcode_tid)); + struct toe_tid_entry *toe_tid; + + toe_tid = lookup_stid(&(TOE_DATA(dev))->tid_maps, stid); + if (toe_tid->ctx && toe_tid->client->handlers && + toe_tid->client->handlers[p->opcode]) { + return toe_tid->client->handlers[p->opcode] (dev, m, toe_tid->ctx); + } else { + log(LOG_ERR, "%s: received clientless CPL command 0x%x\n", + dev->name, p->opcode); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int +do_hwtid_rpl(struct toedev *dev, struct mbuf *m) +{ + union opcode_tid *p = cplhdr(m); + unsigned int hwtid = G_TID(ntohl(p->opcode_tid)); + struct toe_tid_entry *toe_tid; + + toe_tid = lookup_tid(&(TOE_DATA(dev))->tid_maps, hwtid); + if (toe_tid->ctx && toe_tid->client->handlers && + toe_tid->client->handlers[p->opcode]) { + return toe_tid->client->handlers[p->opcode] + (dev, m, toe_tid->ctx); + } else { + log(LOG_ERR, "%s: received clientless CPL command 0x%x\n", + dev->name, p->opcode); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int +do_cr(struct toedev *dev, struct mbuf *m) +{ + struct cpl_pass_accept_req *req = cplhdr(m); + unsigned int stid = G_PASS_OPEN_TID(ntohl(req->tos_tid)); + struct toe_tid_entry *toe_tid; + + toe_tid = lookup_stid(&(TOE_DATA(dev))->tid_maps, stid); + if (toe_tid->ctx && toe_tid->client->handlers && + toe_tid->client->handlers[CPL_PASS_ACCEPT_REQ]) { + return toe_tid->client->handlers[CPL_PASS_ACCEPT_REQ] + (dev, m, toe_tid->ctx); + } else { + log(LOG_ERR, "%s: received clientless CPL command 0x%x\n", + dev->name, CPL_PASS_ACCEPT_REQ); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int +do_abort_req_rss(struct toedev *dev, struct mbuf *m) +{ + union opcode_tid *p = cplhdr(m); + unsigned int hwtid = G_TID(ntohl(p->opcode_tid)); + struct toe_tid_entry *toe_tid; + + toe_tid = lookup_tid(&(TOE_DATA(dev))->tid_maps, hwtid); + if (toe_tid->ctx && toe_tid->client->handlers && + toe_tid->client->handlers[p->opcode]) { + return toe_tid->client->handlers[p->opcode] + (dev, m, toe_tid->ctx); + } else { + struct cpl_abort_req_rss *req = cplhdr(m); + struct cpl_abort_rpl *rpl; + + struct mbuf *m = m_get(M_NOWAIT, MT_DATA); + if (!m) { + log(LOG_NOTICE, "do_abort_req_rss: couldn't get mbuf!\n"); + goto out; + } + + m_set_priority(m, CPL_PRIORITY_DATA); +#if 0 + __skb_put(skb, sizeof(struct cpl_abort_rpl)); +#endif + rpl = cplhdr(m); + rpl->wr.wr_hi = + htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_RPL)); + rpl->wr.wr_lo = htonl(V_WR_TID(GET_TID(req))); + OPCODE_TID(rpl) = + htonl(MK_OPCODE_TID(CPL_ABORT_RPL, GET_TID(req))); + rpl->cmd = req->status; + cxgb_ofld_send(dev, m); + out: + return CPL_RET_BUF_DONE; + } +} + +static int +do_act_establish(struct toedev *dev, struct mbuf *m) +{ + struct cpl_act_establish *req = cplhdr(m); + unsigned int atid = G_PASS_OPEN_TID(ntohl(req->tos_tid)); + struct toe_tid_entry *toe_tid; + + toe_tid = lookup_atid(&(TOE_DATA(dev))->tid_maps, atid); + if (toe_tid->ctx && toe_tid->client->handlers && + toe_tid->client->handlers[CPL_ACT_ESTABLISH]) { + return toe_tid->client->handlers[CPL_ACT_ESTABLISH] + (dev, m, toe_tid->ctx); + } else { + log(LOG_ERR, "%s: received clientless CPL command 0x%x\n", + dev->name, CPL_PASS_ACCEPT_REQ); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int +do_set_tcb_rpl(struct toedev *dev, struct mbuf *m) +{ + struct cpl_set_tcb_rpl *rpl = cplhdr(m); + + if (rpl->status != CPL_ERR_NONE) + log(LOG_ERR, + "Unexpected SET_TCB_RPL status %u for tid %u\n", + rpl->status, GET_TID(rpl)); + return CPL_RET_BUF_DONE; +} + +static int +do_trace(struct toedev *dev, struct mbuf *m) +{ +#if 0 + struct cpl_trace_pkt *p = cplhdr(m); + + + skb->protocol = 0xffff; + skb->dev = dev->lldev; + skb_pull(skb, sizeof(*p)); + skb->mac.raw = mtod(m, (char *)); + netif_receive_skb(skb); +#endif + return 0; +} + +static int +do_term(struct toedev *dev, struct mbuf *m) +{ + unsigned int hwtid = ntohl(m_get_priority(m)) >> 8 & 0xfffff; + unsigned int opcode = G_OPCODE(ntohl(m->m_pkthdr.csum_data)); + struct toe_tid_entry *toe_tid; + + toe_tid = lookup_tid(&(TOE_DATA(dev))->tid_maps, hwtid); + if (toe_tid->ctx && toe_tid->client->handlers && + toe_tid->client->handlers[opcode]) { + return toe_tid->client->handlers[opcode](dev, m, toe_tid->ctx); + } else { + log(LOG_ERR, "%s: received clientless CPL command 0x%x\n", + dev->name, opcode); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } + return (0); +} + +#if defined(FOO) +#include +#include +#include +#include + +static int (*orig_arp_constructor)(struct ifnet *); + +static void +neigh_suspect(struct ifnet *neigh) +{ + struct hh_cache *hh; + + neigh->output = neigh->ops->output; + + for (hh = neigh->hh; hh; hh = hh->hh_next) + hh->hh_output = neigh->ops->output; +} + +static void +neigh_connect(struct ifnet *neigh) +{ + struct hh_cache *hh; + + neigh->output = neigh->ops->connected_output; + + for (hh = neigh->hh; hh; hh = hh->hh_next) + hh->hh_output = neigh->ops->hh_output; +} + +static inline int +neigh_max_probes(const struct neighbour *n) +{ + const struct neigh_parms *p = n->parms; + return (n->nud_state & NUD_PROBE ? + p->ucast_probes : + p->ucast_probes + p->app_probes + p->mcast_probes); +} + +static void +neigh_timer_handler_offload(unsigned long arg) +{ + unsigned long now, next; + struct neighbour *neigh = (struct neighbour *)arg; + unsigned state; + int notify = 0; + + write_lock(&neigh->lock); + + state = neigh->nud_state; + now = jiffies; + next = now + HZ; + + if (!(state & NUD_IN_TIMER)) { +#ifndef CONFIG_SMP + log(LOG_WARNING, "neigh: timer & !nud_in_timer\n"); +#endif + goto out; + } + + if (state & NUD_REACHABLE) { + if (time_before_eq(now, + neigh->confirmed + + neigh->parms->reachable_time)) { + next = neigh->confirmed + neigh->parms->reachable_time; + } else if (time_before_eq(now, + neigh->used + + neigh->parms->delay_probe_time)) { + neigh->nud_state = NUD_DELAY; + neigh->updated = jiffies; + neigh_suspect(neigh); + next = now + neigh->parms->delay_probe_time; + } else { + neigh->nud_state = NUD_STALE; + neigh->updated = jiffies; + neigh_suspect(neigh); + cxgb_neigh_update(neigh); + } + } else if (state & NUD_DELAY) { + if (time_before_eq(now, + neigh->confirmed + + neigh->parms->delay_probe_time)) { + neigh->nud_state = NUD_REACHABLE; + neigh->updated = jiffies; + neigh_connect(neigh); + cxgb_neigh_update(neigh); + next = neigh->confirmed + neigh->parms->reachable_time; + } else { + neigh->nud_state = NUD_PROBE; + neigh->updated = jiffies; + atomic_set_int(&neigh->probes, 0); + next = now + neigh->parms->retrans_time; + } + } else { + /* NUD_PROBE|NUD_INCOMPLETE */ + next = now + neigh->parms->retrans_time; + } + /* + * Needed for read of probes + */ + mb(); + if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && + neigh->probes >= neigh_max_probes(neigh)) { + struct mbuf *m; + + neigh->nud_state = NUD_FAILED; + neigh->updated = jiffies; + notify = 1; + cxgb_neigh_update(neigh); + NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed); + + /* It is very thin place. report_unreachable is very + complicated routine. Particularly, it can hit the same + neighbour entry! + So that, we try to be accurate and avoid dead loop. --ANK + */ + while (neigh->nud_state == NUD_FAILED && + (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { + write_unlock(&neigh->lock); + neigh->ops->error_report(neigh, skb); + write_lock(&neigh->lock); + } + skb_queue_purge(&neigh->arp_queue); + } + + if (neigh->nud_state & NUD_IN_TIMER) { + if (time_before(next, jiffies + HZ/2)) + next = jiffies + HZ/2; + if (!mod_timer(&neigh->timer, next)) + neigh_hold(neigh); + } + if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) { + struct mbuf *m = skb_peek(&neigh->arp_queue); + + write_unlock(&neigh->lock); + neigh->ops->solicit(neigh, skb); + atomic_add_int(&neigh->probes, 1); + if (m) + m_free(m); + } else { +out: + write_unlock(&neigh->lock); + } + +#ifdef CONFIG_ARPD + if (notify && neigh->parms->app_probes) + neigh_app_notify(neigh); +#endif + neigh_release(neigh); +} + +static int +arp_constructor_offload(struct neighbour *neigh) +{ + if (neigh->ifp && is_offloading(neigh->ifp)) + neigh->timer.function = neigh_timer_handler_offload; + return orig_arp_constructor(neigh); +} + +/* + * This must match exactly the signature of neigh_update for jprobes to work. + * It runs from a trap handler with interrupts off so don't disable BH. + */ +static int +neigh_update_offload(struct neighbour *neigh, const u8 *lladdr, + u8 new, u32 flags) +{ + write_lock(&neigh->lock); + cxgb_neigh_update(neigh); + write_unlock(&neigh->lock); + jprobe_return(); + /* NOTREACHED */ + return 0; +} + +static struct jprobe neigh_update_jprobe = { + .entry = (kprobe_opcode_t *) neigh_update_offload, + .kp.addr = (kprobe_opcode_t *) neigh_update +}; + +#ifdef MODULE_SUPPORT +static int +prepare_arp_with_t3core(void) +{ + int err; + + err = register_jprobe(&neigh_update_jprobe); + if (err) { + log(LOG_ERR, "Could not install neigh_update jprobe, " + "error %d\n", err); + return err; + } + + orig_arp_constructor = arp_tbl.constructor; + arp_tbl.constructor = arp_constructor_offload; + + return 0; +} + +static void +restore_arp_sans_t3core(void) +{ + arp_tbl.constructor = orig_arp_constructor; + unregister_jprobe(&neigh_update_jprobe); +} + +#else /* Module suport */ +static inline int +prepare_arp_with_t3core(void) +{ + return 0; +} + +static inline void +restore_arp_sans_t3core(void) +{} +#endif +#endif +/* + * Process a received packet with an unknown/unexpected CPL opcode. + */ +static int +do_bad_cpl(struct toedev *dev, struct mbuf *m) +{ + log(LOG_ERR, "%s: received bad CPL command 0x%x\n", dev->name, + *mtod(m, uint32_t *)); + return (CPL_RET_BUF_DONE | CPL_RET_BAD_MSG); +} + +/* + * Handlers for each CPL opcode + */ +static cpl_handler_func cpl_handlers[NUM_CPL_CMDS]; + +/* + * Add a new handler to the CPL dispatch table. A NULL handler may be supplied + * to unregister an existing handler. + */ +void +t3_register_cpl_handler(unsigned int opcode, cpl_handler_func h) +{ + if (opcode < NUM_CPL_CMDS) + cpl_handlers[opcode] = h ? h : do_bad_cpl; + else + log(LOG_ERR, "T3C: handler registration for " + "opcode %x failed\n", opcode); +} + +/* + * TOEDEV's receive method. + */ +int +process_rx(struct toedev *dev, struct mbuf **m, int n) +{ + while (n--) { + struct mbuf *m0 = *m++; + unsigned int opcode = G_OPCODE(ntohl(m0->m_pkthdr.csum_data)); + int ret = cpl_handlers[opcode] (dev, m0); + +#if VALIDATE_TID + if (ret & CPL_RET_UNKNOWN_TID) { + union opcode_tid *p = cplhdr(m0); + + log(LOG_ERR, "%s: CPL message (opcode %u) had " + "unknown TID %u\n", dev->name, opcode, + G_TID(ntohl(p->opcode_tid))); + } +#endif + if (ret & CPL_RET_BUF_DONE) + m_freem(m0); + } + return 0; +} + +/* + * Sends an sk_buff to a T3C driver after dealing with any active network taps. + */ +int +cxgb_ofld_send(struct toedev *dev, struct mbuf *m) +{ + int r; + + critical_enter(); + r = dev->send(dev, m); + critical_exit(); + return r; +} + + +/** + * cxgb_ofld_recv - process n received offload packets + * @dev: the offload device + * @m: an array of offload packets + * @n: the number of offload packets + * + * Process an array of ingress offload packets. Each packet is forwarded + * to any active network taps and then passed to the offload device's receive + * method. We optimize passing packets to the receive method by passing + * it the whole array at once except when there are active taps. + */ +int +cxgb_ofld_recv(struct toedev *dev, struct mbuf **m, int n) +{ + +#if defined(CONFIG_CHELSIO_T3) + if (likely(!netdev_nit)) + return dev->recv(dev, skb, n); + + for ( ; n; n--, skb++) { + skb[0]->dev = dev->lldev; + dev_queue_xmit_nit(skb[0], dev->lldev); + skb[0]->dev = NULL; + dev->recv(dev, skb, 1); + } + return 0; +#else + return dev->recv(dev, m, n); +#endif +} + +void +cxgb_neigh_update(struct rtentry *rt) +{ + + if (is_offloading(rt->rt_ifp)) { + struct toedev *tdev = TOEDEV(rt->rt_ifp); + + BUG_ON(!tdev); + t3_l2t_update(tdev, rt); + } +} + +static void +set_l2t_ix(struct toedev *tdev, u32 tid, struct l2t_entry *e) +{ + struct mbuf *m; + struct cpl_set_tcb_field *req; + + m = m_gethdr(M_NOWAIT, MT_DATA); + if (!m) { + log(LOG_ERR, "%s: cannot allocate mbuf!\n", __FUNCTION__); + return; + } + + m_set_priority(m, CPL_PRIORITY_CONTROL); + req = mtod(m, struct cpl_set_tcb_field *); + req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); + OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid)); + req->reply = 0; + req->cpu_idx = 0; + req->word = htons(W_TCB_L2T_IX); + req->mask = htobe64(V_TCB_L2T_IX(M_TCB_L2T_IX)); + req->val = htobe64(V_TCB_L2T_IX(e->idx)); + tdev->send(tdev, m); +} + +void +cxgb_redirect(struct rtentry *old, struct rtentry *new) +{ + struct ifnet *olddev, *newdev; + struct tid_info *ti; + struct toedev *tdev; + u32 tid; + int update_tcb; + struct l2t_entry *e; + struct toe_tid_entry *te; + + olddev = old->rt_ifp; + newdev = new->rt_ifp; + if (!is_offloading(olddev)) + return; + if (!is_offloading(newdev)) { + log(LOG_WARNING, "%s: Redirect to non-offload" + "device ignored.\n", __FUNCTION__); + return; + } + tdev = TOEDEV(olddev); + BUG_ON(!tdev); + if (tdev != TOEDEV(newdev)) { + log(LOG_WARNING, "%s: Redirect to different " + "offload device ignored.\n", __FUNCTION__); + return; + } + + /* Add new L2T entry */ + e = t3_l2t_get(tdev, new, ((struct port_info *)new->rt_ifp->if_softc)->port); + if (!e) { + log(LOG_ERR, "%s: couldn't allocate new l2t entry!\n", + __FUNCTION__); + return; + } + + /* Walk tid table and notify clients of dst change. */ + ti = &(TOE_DATA(tdev))->tid_maps; + for (tid=0; tid < ti->ntids; tid++) { + te = lookup_tid(ti, tid); + BUG_ON(!te); + if (te->ctx && te->client && te->client->redirect) { + update_tcb = te->client->redirect(te->ctx, old, new, + e); + if (update_tcb) { + l2t_hold(L2DATA(tdev), e); + set_l2t_ix(tdev, tid, e); + } + } + } + l2t_release(L2DATA(tdev), e); +} + +/* + * Allocate a chunk of memory using kmalloc or, if that fails, vmalloc. + * The allocated memory is cleared. + */ +void * +cxgb_alloc_mem(unsigned long size) +{ + + return malloc(size, M_DEVBUF, M_ZERO); +} + +/* + * Free memory allocated through t3_alloc_mem(). + */ +void +cxgb_free_mem(void *addr) +{ + free(addr, M_DEVBUF); +} + + +/* + * Allocate and initialize the TID tables. Returns 0 on success. + */ +static int +init_tid_tabs(struct tid_info *t, unsigned int ntids, + unsigned int natids, unsigned int nstids, + unsigned int atid_base, unsigned int stid_base) +{ + unsigned long size = ntids * sizeof(*t->tid_tab) + + natids * sizeof(*t->atid_tab) + nstids * sizeof(*t->stid_tab); + + t->tid_tab = cxgb_alloc_mem(size); + if (!t->tid_tab) + return -ENOMEM; + + t->stid_tab = (union listen_entry *)&t->tid_tab[ntids]; + t->atid_tab = (union active_open_entry *)&t->stid_tab[nstids]; + t->ntids = ntids; + t->nstids = nstids; + t->stid_base = stid_base; + t->sfree = NULL; + t->natids = natids; + t->atid_base = atid_base; + t->afree = NULL; + t->stids_in_use = t->atids_in_use = 0; + atomic_set_int(&t->tids_in_use, 0); + mtx_init(&t->stid_lock, "stid", NULL, MTX_DEF); + mtx_init(&t->atid_lock, "atid", NULL, MTX_DEF); + + /* + * Setup the free lists for stid_tab and atid_tab. + */ + if (nstids) { + while (--nstids) + t->stid_tab[nstids - 1].next = &t->stid_tab[nstids]; + t->sfree = t->stid_tab; + } + if (natids) { + while (--natids) + t->atid_tab[natids - 1].next = &t->atid_tab[natids]; + t->afree = t->atid_tab; + } + return 0; +} + +static void +free_tid_maps(struct tid_info *t) +{ + cxgb_free_mem(t->tid_tab); +} + +static inline void +add_adapter(adapter_t *adap) +{ + rw_wlock(&adapter_list_lock); + TAILQ_INSERT_TAIL(&adapter_list, adap, adapter_entry); + rw_wunlock(&adapter_list_lock); +} + +static inline void +remove_adapter(adapter_t *adap) +{ + rw_wlock(&adapter_list_lock); + TAILQ_REMOVE(&adapter_list, adap, adapter_entry); + rw_wunlock(&adapter_list_lock); +} + +/* + * XXX + */ +#define t3_free_l2t(...) + +int +cxgb_offload_activate(struct adapter *adapter) +{ + struct toedev *dev = &adapter->tdev; + int natids, err; + struct toe_data *t; + struct tid_range stid_range, tid_range; + struct mtutab mtutab; + unsigned int l2t_capacity; + + t = malloc(sizeof(*t), M_DEVBUF, M_WAITOK); + if (!t) + return (ENOMEM); + + err = (EOPNOTSUPP); + if (dev->ctl(dev, GET_TX_MAX_CHUNK, &t->tx_max_chunk) < 0 || + dev->ctl(dev, GET_MAX_OUTSTANDING_WR, &t->max_wrs) < 0 || + dev->ctl(dev, GET_L2T_CAPACITY, &l2t_capacity) < 0 || + dev->ctl(dev, GET_MTUS, &mtutab) < 0 || + dev->ctl(dev, GET_TID_RANGE, &tid_range) < 0 || + dev->ctl(dev, GET_STID_RANGE, &stid_range) < 0) + goto out_free; + + err = (ENOMEM); + L2DATA(dev) = t3_init_l2t(l2t_capacity); + if (!L2DATA(dev)) + goto out_free; + + natids = min(tid_range.num / 2, MAX_ATIDS); + err = init_tid_tabs(&t->tid_maps, tid_range.num, natids, + stid_range.num, ATID_BASE, stid_range.base); + if (err) + goto out_free_l2t; + + t->mtus = mtutab.mtus; + t->nmtus = mtutab.size; + + TASK_INIT(&t->tid_release_task, 0 /* XXX? */, t3_process_tid_release_list, dev); + mtx_init(&t->tid_release_lock, "tid release", NULL, MTX_DEF); + t->dev = dev; + + TOE_DATA(dev) = t; + dev->recv = process_rx; + dev->neigh_update = t3_l2t_update; +#if 0 + offload_proc_dev_setup(dev); +#endif + /* Register netevent handler once */ + if (TAILQ_EMPTY(&adapter_list)) { +#if defined(CONFIG_CHELSIO_T3_MODULE) + if (prepare_arp_with_t3core()) + log(LOG_ERR, "Unable to set offload capabilities\n"); +#endif + } + add_adapter(adapter); + return 0; + +out_free_l2t: + t3_free_l2t(L2DATA(dev)); + L2DATA(dev) = NULL; +out_free: + free(t, M_DEVBUF); + return err; + +} + +void +cxgb_offload_deactivate(struct adapter *adapter) +{ + struct toedev *tdev = &adapter->tdev; + struct toe_data *t = TOE_DATA(tdev); + + remove_adapter(adapter); + if (TAILQ_EMPTY(&adapter_list)) { +#if defined(CONFIG_CHELSIO_T3_MODULE) + restore_arp_sans_t3core(); +#endif + } + free_tid_maps(&t->tid_maps); + TOE_DATA(tdev) = NULL; + t3_free_l2t(L2DATA(tdev)); + L2DATA(tdev) = NULL; + free(t, M_DEVBUF); +} + + +static inline void +register_tdev(struct toedev *tdev) +{ + static int unit; + + mtx_lock(&cxgb_db_lock); + snprintf(tdev->name, sizeof(tdev->name), "ofld_dev%d", unit++); + TAILQ_INSERT_TAIL(&ofld_dev_list, tdev, ofld_entry); + mtx_unlock(&cxgb_db_lock); +} + +static inline void +unregister_tdev(struct toedev *tdev) +{ + mtx_lock(&cxgb_db_lock); + TAILQ_REMOVE(&ofld_dev_list, tdev, ofld_entry); + mtx_unlock(&cxgb_db_lock); +} + +void +cxgb_adapter_ofld(struct adapter *adapter) +{ + struct toedev *tdev = &adapter->tdev; + + cxgb_set_dummy_ops(tdev); + tdev->send = t3_offload_tx; + tdev->ctl = cxgb_offload_ctl; + tdev->type = adapter->params.rev == 0 ? + T3A : T3B; + + register_tdev(tdev); +#if 0 + offload_proc_dev_init(tdev); +#endif +} + +void +cxgb_adapter_unofld(struct adapter *adapter) +{ + struct toedev *tdev = &adapter->tdev; +#if 0 + offload_proc_dev_cleanup(tdev); + offload_proc_dev_exit(tdev); +#endif + tdev->recv = NULL; + tdev->neigh_update = NULL; + + unregister_tdev(tdev); +} + +void +cxgb_offload_init(void) +{ + int i; + + if (inited) + return; + else + inited = 1; + + mtx_init(&cxgb_db_lock, "ofld db", NULL, MTX_DEF); + rw_init(&adapter_list_lock, "ofld adap list"); + TAILQ_INIT(&client_list); + TAILQ_INIT(&ofld_dev_list); + TAILQ_INIT(&adapter_list); + + for (i = 0; i < NUM_CPL_CMDS; ++i) + cpl_handlers[i] = do_bad_cpl; + + t3_register_cpl_handler(CPL_SMT_WRITE_RPL, do_smt_write_rpl); + t3_register_cpl_handler(CPL_L2T_WRITE_RPL, do_l2t_write_rpl); + t3_register_cpl_handler(CPL_PASS_OPEN_RPL, do_stid_rpl); + t3_register_cpl_handler(CPL_CLOSE_LISTSRV_RPL, do_stid_rpl); + t3_register_cpl_handler(CPL_PASS_ACCEPT_REQ, do_cr); + t3_register_cpl_handler(CPL_PASS_ESTABLISH, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ABORT_RPL_RSS, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ABORT_RPL, do_hwtid_rpl); + t3_register_cpl_handler(CPL_RX_URG_NOTIFY, do_hwtid_rpl); + t3_register_cpl_handler(CPL_RX_DATA, do_hwtid_rpl); + t3_register_cpl_handler(CPL_TX_DATA_ACK, do_hwtid_rpl); + t3_register_cpl_handler(CPL_TX_DMA_ACK, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ACT_OPEN_RPL, do_act_open_rpl); + t3_register_cpl_handler(CPL_PEER_CLOSE, do_hwtid_rpl); + t3_register_cpl_handler(CPL_CLOSE_CON_RPL, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ABORT_REQ_RSS, do_abort_req_rss); + t3_register_cpl_handler(CPL_ACT_ESTABLISH, do_act_establish); + t3_register_cpl_handler(CPL_SET_TCB_RPL, do_set_tcb_rpl); + t3_register_cpl_handler(CPL_RDMA_TERMINATE, do_term); + t3_register_cpl_handler(CPL_RDMA_EC_STATUS, do_hwtid_rpl); + t3_register_cpl_handler(CPL_TRACE_PKT, do_trace); + t3_register_cpl_handler(CPL_RX_DATA_DDP, do_hwtid_rpl); + t3_register_cpl_handler(CPL_RX_DDP_COMPLETE, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ISCSI_HDR, do_hwtid_rpl); +#if 0 + if (offload_proc_init()) + log(LOG_WARNING, "Unable to create /proc/net/cxgb3 dir\n"); +#endif +} + +void +cxgb_offload_exit(void) +{ +#if 0 + offload_proc_cleanup(); +#endif +} + +#if 0 +static int +offload_info_read_proc(char *buf, char **start, off_t offset, + int length, int *eof, void *data) +{ + struct toe_data *d = data; + struct tid_info *t = &d->tid_maps; + int len; + + len = sprintf(buf, "TID range: 0..%d, in use: %u\n" + "STID range: %d..%d, in use: %u\n" + "ATID range: %d..%d, in use: %u\n" + "MSS: %u\n", + t->ntids - 1, atomic_read(&t->tids_in_use), t->stid_base, + t->stid_base + t->nstids - 1, t->stids_in_use, + t->atid_base, t->atid_base + t->natids - 1, + t->atids_in_use, d->tx_max_chunk); + if (len > length) + len = length; + *eof = 1; + return len; +} + +static int +offload_info_proc_setup(struct proc_dir_entry *dir, + struct toe_data *d) +{ + struct proc_dir_entry *p; + + if (!dir) + return -EINVAL; + + p = create_proc_read_entry("info", 0, dir, offload_info_read_proc, d); + if (!p) + return -ENOMEM; + + p->owner = THIS_MODULE; + return 0; +} + + +static int +offload_devices_read_proc(char *buf, char **start, off_t offset, + int length, int *eof, void *data) +{ + int len; + struct toedev *dev; + struct net_device *ndev; + + len = sprintf(buf, "Device Interfaces\n"); + + mtx_lock(&cxgb_db_lock); + TAILQ_FOREACH(dev, &ofld_dev_list, ofld_entry) { + len += sprintf(buf + len, "%-16s", dev->name); + read_lock(&dev_base_lock); + for (ndev = dev_base; ndev; ndev = ndev->next) { + if (TOEDEV(ndev) == dev) + len += sprintf(buf + len, " %s", ndev->name); + } + read_unlock(&dev_base_lock); + len += sprintf(buf + len, "\n"); + if (len >= length) + break; + } + mtx_unlock(&cxgb_db_lock); + + if (len > length) + len = length; + *eof = 1; + return len; +} + +#endif + Property changes on: stable/6/sys/dev/cxgb/cxgb_offload.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/cxgb_offload.h =================================================================== --- stable/6/sys/dev/cxgb/cxgb_offload.h (nonexistent) +++ stable/6/sys/dev/cxgb/cxgb_offload.h (revision 170009) @@ -0,0 +1,260 @@ + +/************************************************************************** + +Copyright (c) 2007, Chelsio Inc. +All rights reserved. + +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. Neither the name of the Chelsio Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$FreeBSD$ + +***************************************************************************/ + +#ifndef _CXGB_OFFLOAD_H +#define _CXGB_OFFLOAD_H + + +#include +#include + +#include +#include + +struct adapter; +struct cxgb_client; + +void cxgb_offload_init(void); +void cxgb_offload_exit(void); + +void cxgb_adapter_ofld(struct adapter *adapter); +void cxgb_adapter_unofld(struct adapter *adapter); +int cxgb_offload_activate(struct adapter *adapter); +void cxgb_offload_deactivate(struct adapter *adapter); +int cxgb_ofld_recv(struct toedev *dev, struct mbuf **m, int n); + +void cxgb_set_dummy_ops(struct toedev *dev); + + +/* + * Client registration. Users of T3 driver must register themselves. + * The T3 driver will call the add function of every client for each T3 + * adapter activated, passing up the toedev ptr. Each client fills out an + * array of callback functions to process CPL messages. + */ + +void cxgb_register_client(struct cxgb_client *client); +void cxgb_unregister_client(struct cxgb_client *client); +void cxgb_add_clients(struct toedev *tdev); +void cxgb_remove_clients(struct toedev *tdev); + +typedef int (*cxgb_cpl_handler_func)(struct toedev *dev, + struct mbuf *m, void *ctx); + +struct cxgb_client { + char *name; + void (*add) (struct toedev *); + void (*remove) (struct toedev *); + cxgb_cpl_handler_func *handlers; + int (*redirect)(void *ctx, struct rtentry *old, + struct rtentry *new, + struct l2t_entry *l2t); + TAILQ_ENTRY(cxgb_client) client_entry; +}; + +/* + * TID allocation services. + */ +int cxgb_alloc_atid(struct toedev *dev, struct cxgb_client *client, + void *ctx); +int cxgb_alloc_stid(struct toedev *dev, struct cxgb_client *client, + void *ctx); +void *cxgb_free_atid(struct toedev *dev, int atid); +void cxgb_free_stid(struct toedev *dev, int stid); +void cxgb_insert_tid(struct toedev *dev, struct cxgb_client *client, + void *ctx, + unsigned int tid); +void cxgb_queue_tid_release(struct toedev *dev, unsigned int tid); +void cxgb_remove_tid(struct toedev *dev, void *ctx, unsigned int tid); + +struct toe_tid_entry { + struct cxgb_client *client; + void *ctx; +}; + +/* CPL message priority levels */ +enum { + CPL_PRIORITY_DATA = 0, /* data messages */ + CPL_PRIORITY_SETUP = 1, /* connection setup messages */ + CPL_PRIORITY_TEARDOWN = 0, /* connection teardown messages */ + CPL_PRIORITY_LISTEN = 1, /* listen start/stop messages */ + CPL_PRIORITY_ACK = 1, /* RX ACK messages */ + CPL_PRIORITY_CONTROL = 1 /* offload control messages */ +}; + +/* Flags for return value of CPL message handlers */ +enum { + CPL_RET_BUF_DONE = 1, // buffer processing done, buffer may be freed + CPL_RET_BAD_MSG = 2, // bad CPL message (e.g., unknown opcode) + CPL_RET_UNKNOWN_TID = 4 // unexpected unknown TID +}; + +typedef int (*cpl_handler_func)(struct toedev *dev, struct mbuf *m); + +/* + * Returns a pointer to the first byte of the CPL header in an sk_buff that + * contains a CPL message. + */ +static inline void *cplhdr(struct mbuf *m) +{ + return m->m_data; +} + +void t3_register_cpl_handler(unsigned int opcode, cpl_handler_func h); + +union listen_entry { + struct toe_tid_entry toe_tid; + union listen_entry *next; +}; + +union active_open_entry { + struct toe_tid_entry toe_tid; + union active_open_entry *next; +}; + +/* + * Holds the size, base address, free list start, etc of the TID, server TID, + * and active-open TID tables for a offload device. + * The tables themselves are allocated dynamically. + */ +struct tid_info { + struct toe_tid_entry *tid_tab; + unsigned int ntids; + volatile int tids_in_use; + + union listen_entry *stid_tab; + unsigned int nstids; + unsigned int stid_base; + + union active_open_entry *atid_tab; + unsigned int natids; + unsigned int atid_base; + + /* + * The following members are accessed R/W so we put them in their own + * cache lines. + * + * XXX We could combine the atid fields above with the lock here since + * atids are use once (unlike other tids). OTOH the above fields are + * usually in cache due to tid_tab. + */ + struct mtx atid_lock /* ____cacheline_aligned_in_smp */; + union active_open_entry *afree; + unsigned int atids_in_use; + + struct mtx stid_lock /*____cacheline_aligned */; + union listen_entry *sfree; + unsigned int stids_in_use; +}; + +struct toe_data { +#ifdef notyet + struct list_head list_node; +#endif + struct toedev *dev; + unsigned int tx_max_chunk; /* max payload for TX_DATA */ + unsigned int max_wrs; /* max in-flight WRs per connection */ + unsigned int nmtus; + const unsigned short *mtus; + struct tid_info tid_maps; + + struct toe_tid_entry *tid_release_list; + struct mtx tid_release_lock; + struct task tid_release_task; +}; + +/* + * toedev -> toe_data accessor + */ +#define TOE_DATA(dev) (*(struct toe_data **)&(dev)->l4opt) + +/* + * Map an ATID or STID to their entries in the corresponding TID tables. + */ +static inline union active_open_entry *atid2entry(const struct tid_info *t, + unsigned int atid) +{ + return &t->atid_tab[atid - t->atid_base]; +} + + +static inline union listen_entry *stid2entry(const struct tid_info *t, + unsigned int stid) +{ + return &t->stid_tab[stid - t->stid_base]; +} + +/* + * Find the connection corresponding to a TID. + */ +static inline struct toe_tid_entry *lookup_tid(const struct tid_info *t, + unsigned int tid) +{ + return tid < t->ntids ? &(t->tid_tab[tid]) : NULL; +} + +/* + * Find the connection corresponding to a server TID. + */ +static inline struct toe_tid_entry *lookup_stid(const struct tid_info *t, + unsigned int tid) +{ + if (tid < t->stid_base || tid >= t->stid_base + t->nstids) + return NULL; + return &(stid2entry(t, tid)->toe_tid); +} + +/* + * Find the connection corresponding to an active-open TID. + */ +static inline struct toe_tid_entry *lookup_atid(const struct tid_info *t, + unsigned int tid) +{ + if (tid < t->atid_base || tid >= t->atid_base + t->natids) + return NULL; + return &(atid2entry(t, tid)->toe_tid); +} + +void *cxgb_alloc_mem(unsigned long size); +void cxgb_free_mem(void *addr); +void cxgb_neigh_update(struct rtentry *rt); +void cxgb_redirect(struct rtentry *old, struct rtentry *new); +int process_rx(struct toedev *dev, struct mbuf **m, int n); +int attach_toedev(struct toedev *dev); +void detach_toedev(struct toedev *dev); + + +#endif Property changes on: stable/6/sys/dev/cxgb/cxgb_offload.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/sys/mvec.h =================================================================== --- stable/6/sys/dev/cxgb/sys/mvec.h (nonexistent) +++ stable/6/sys/dev/cxgb/sys/mvec.h (revision 170009) @@ -0,0 +1,190 @@ +/************************************************************************** + * + * Copyright (c) 2007, Kip Macy kmacy@freebsd.org + * All rights reserved. + * + * 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. The name of Kip Macy nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + * + * $FreeBSD$ + * + ***************************************************************************/ + +#ifndef _MVEC_H_ +#define _MVEC_H_ + +#define mtomv(m) ((struct mbuf_vec *)((m)->m_pktdat)) + +#define M_IOVEC 0x100000 /* mbuf immediate data area is used for cluster ptrs */ +#define MBUF_IOV_TYPE_MASK ((1<<3)-1) +#define mbuf_vec_set_type(mv, i, type) \ + (mv)->mv_vec[(i)].mi_flags = (((mv)->mv_vec[(i)].mi_flags \ + & ~MBUF_IOV_TYPE_MASK) | type) + +#define mbuf_vec_get_type(mv, i) \ + ((mv)->mv_vec[(i)].mi_flags & MBUF_IOV_TYPE_MASK) + + +struct mbuf_iovec { + uint16_t mi_flags; /* per-cluster flags */ + uint16_t mi_len; /* length of cluster */ + uint32_t mi_offset; /* data offsets into cluster */ + uint8_t *mi_base; /* pointers to cluster */ + volatile uint32_t *mi_refcnt; /* refcnt for cluster*/ +#ifdef __i386__ + void *mi_args; /* for sf_buf */ +#endif +}; + +#define MAX_MBUF_IOV ((MHLEN-8)/sizeof(struct mbuf_iovec)) +struct mbuf_vec { + uint16_t mv_first; /* first valid cluster */ + uint16_t mv_count; /* # of clusters */ + uint32_t mv_flags; /* flags for iovec */ + struct mbuf_iovec mv_vec[MAX_MBUF_IOV]; +}; + +int _m_explode(struct mbuf *); +int _m_collapse(struct mbuf *, int maxbufs, struct mbuf **); +void mb_free_vec(struct mbuf *m); + +static __inline void +m_iovinit(struct mbuf *m) +{ + struct mbuf_vec *mv = mtomv(m); + + mv->mv_first = mv->mv_count = 0; + m->m_pkthdr.len = m->m_len = 0; + m->m_flags |= M_IOVEC; +} + +static __inline void +m_iovappend(struct mbuf *m, uint8_t *cl, int size, int len, int offset) +{ + struct mbuf_vec *mv = mtomv(m); + struct mbuf_iovec *iov; + int idx = mv->mv_first + mv->mv_count; + + KASSERT(idx <= MAX_MBUF_IOV, ("tried to append too many clusters to mbuf iovec")); + if ((m->m_flags & M_EXT) != 0) + panic("invalid flags in %s", __func__); + + if (mv->mv_count == 0) + m->m_data = cl + offset; + + iov = &mv->mv_vec[idx]; + iov->mi_flags = m_gettype(size); + iov->mi_base = cl; + iov->mi_len = len; + iov->mi_offset = offset; + m->m_pkthdr.len += len; + m->m_len += len; + mv->mv_count++; +} + +static __inline int +m_explode(struct mbuf *m) +{ + if ((m->m_flags & M_IOVEC) == 0) + return (0); + + return _m_explode(m); +} + +static __inline int +m_collapse(struct mbuf *m, int maxbufs, struct mbuf **mnew) +{ +#if (!defined(__sparc64__) && !defined(__sun4v__)) + if (m->m_next == NULL) +#endif + { + *mnew = m; + return (0); + } + return _m_collapse(m, maxbufs, mnew); +} + +static __inline struct mbuf * +m_free_vec(struct mbuf *m) +{ + struct mbuf *n = m->m_next; + + if (m->m_flags & M_IOVEC) + mb_free_vec(m); + else if (m->m_flags & M_EXT) + mb_free_ext(m); + else + uma_zfree(zone_mbuf, m); + return (n); +} + +static __inline void +m_freem_vec(struct mbuf *m) +{ + while (m != NULL) + m = m_free_vec(m); +} + +static __inline uma_zone_t +m_getzonefromtype(int type) +{ + uma_zone_t zone; + + switch (type) { + case EXT_MBUF: + zone = zone_mbuf; + break; + case EXT_CLUSTER: + zone = zone_clust; + break; +#if MJUMPAGESIZE != MCLBYTES + case EXT_JUMBOP: + zone = zone_jumbop; + break; +#endif + case EXT_JUMBO9: + zone = zone_jumbo9; + break; + case EXT_JUMBO16: + zone = zone_jumbo16; + break; +#ifndef PACKET_ZONE_DISABLED + case EXT_PACKET: + zone = zone_pack; + break; +#endif + default: + panic("%s: invalid cluster type %d", __func__, type); + } + return (zone); +} + +#if (!defined(__sparc64__) && !defined(__sun4v__)) +int +bus_dmamap_load_mvec_sg(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m0, + bus_dma_segment_t *segs, int *nsegs, int flags); + +#else +#define bus_dmamap_load_mvec_sg bus_dmamap_load_mbuf_sg +#endif + +#endif Property changes on: stable/6/sys/dev/cxgb/sys/mvec.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/sys/uipc_mvec.c =================================================================== --- stable/6/sys/dev/cxgb/sys/uipc_mvec.c (nonexistent) +++ stable/6/sys/dev/cxgb/sys/uipc_mvec.c (revision 170009) @@ -0,0 +1,617 @@ +/************************************************************************** + * + * Copyright (c) 2007, Kip Macy kmacy@freebsd.org + * All rights reserved. + * + * 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. The name of Kip Macy nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + * + * + ***************************************************************************/ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "opt_zero.h" + +#include +#include +#include + +#ifdef DEBUG +#define DPRINTF printf +#else +#define DPRINTF(...) +#endif + +#ifdef INVARIANTS +#define M_SANITY m_sanity +#else +#define M_SANITY(a, b) +#endif + +#define MAX_BUFS 36 +#define MAX_HVEC 8 + +extern uint32_t collapse_free; +extern uint32_t mb_free_vec_free; + +struct mbuf_ext { + struct mbuf *me_m; + caddr_t me_base; + volatile u_int *me_refcnt; + int me_flags; + uint32_t me_offset; +}; + +int +_m_explode(struct mbuf *m) +{ + int i, offset, type, first, len; + uint8_t *cl; + struct mbuf *m0, *head = NULL; + struct mbuf_vec *mv; + +#ifdef INVARIANTS + len = m->m_len; + m0 = m->m_next; + while (m0) { + KASSERT((m0->m_flags & M_PKTHDR) == 0, + ("pkthdr set on intermediate mbuf - pre")); + len += m0->m_len; + m0 = m0->m_next; + + } + if (len != m->m_pkthdr.len) + panic("at start len=%d pktlen=%d", len, m->m_pkthdr.len); +#endif + mv = mtomv(m); + first = mv->mv_first; + for (i = mv->mv_count + first - 1; i > first; i--) { + type = mbuf_vec_get_type(mv, i); + cl = mv->mv_vec[i].mi_base; + offset = mv->mv_vec[i].mi_offset; + len = mv->mv_vec[i].mi_len; + if (__predict_false(type == EXT_MBUF)) { + m0 = (struct mbuf *)cl; + KASSERT((m0->m_flags & M_EXT) == 0, ("M_EXT set on mbuf")); + m0->m_len = len; + m0->m_data = cl + offset; + goto skip_cluster; + + } else if ((m0 = m_get(M_NOWAIT, MT_DATA)) == NULL) { + /* + * Check for extra memory leaks + */ + m_freem(head); + return (ENOMEM); + } + m0->m_flags = 0; + + m_cljset(m0, (uint8_t *)cl, type); + m0->m_len = mv->mv_vec[i].mi_len; + if (offset) + m_adj(m0, offset); + skip_cluster: + m0->m_next = head; + m->m_len -= m0->m_len; + head = m0; + } + offset = mv->mv_vec[first].mi_offset; + cl = mv->mv_vec[first].mi_base; + type = mbuf_vec_get_type(mv, first); + m->m_flags &= ~(M_IOVEC); + m_cljset(m, cl, type); + if (offset) + m_adj(m, offset); + m->m_next = head; + head = m; + M_SANITY(m, 0); + + return (0); +} + +static __inline int +m_vectorize(struct mbuf *m, int max, struct mbuf **vec, int *count) +{ + int i, error = 0; + + for (i = 0; i < max; i++) { + if (m == NULL) + break; +#ifndef MBUF_PACKET_ZONE_DISABLE + if ((m->m_flags & M_EXT) && (m->m_ext.ext_type == EXT_PACKET)) + return (EINVAL); +#endif +#ifdef ZERO_COPY_SOCKETS + if ((m->m_flags & M_EXT) && (m->m_ext.ext_type == EXT_SFBUF)) + return (EINVAL); +#endif + M_SANITY(m, 0); + vec[i] = m; + m = m->m_next; + } + if (m) + error = EFBIG; + + *count = i; + + return (error); +} + +static __inline int +m_findmbufs(struct mbuf **ivec, int maxbufs, struct mbuf_ext *ovec, int osize, int *ocount) +{ + int i, j, nhbufsneed, nhbufs; + struct mbuf *m; + + nhbufsneed = min(((maxbufs - 1)/MAX_MBUF_IOV) + 1, osize); + ovec[0].me_m = NULL; + + for (nhbufs = j = i = 0; i < maxbufs && nhbufs < nhbufsneed; i++) { + if ((ivec[i]->m_flags & M_EXT) == 0) + continue; + m = ivec[i]; + ovec[nhbufs].me_m = m; + ovec[nhbufs].me_base = m->m_ext.ext_buf; + ovec[nhbufs].me_refcnt = m->m_ext.ref_cnt; + ovec[nhbufs].me_offset = (m->m_data - m->m_ext.ext_buf); + ovec[nhbufs].me_flags = m->m_ext.ext_type; + nhbufs++; + } + if (nhbufs == 0) { + if ((m = m_gethdr(M_NOWAIT, MT_DATA)) == NULL) + goto m_getfail; + ovec[nhbufs].me_m = m; + nhbufs = 1; + } + while (nhbufs < nhbufsneed) { + if ((m = m_get(M_NOWAIT, MT_DATA)) == NULL) + goto m_getfail; + ovec[nhbufs].me_m = m; + nhbufs++; + } + /* + * Copy over packet header to new head of chain + */ + if (ovec[0].me_m != ivec[0]) { + ovec[0].me_m->m_flags |= M_PKTHDR; + memcpy(&ovec[0].me_m->m_pkthdr, &ivec[0]->m_pkthdr, sizeof(struct pkthdr)); + SLIST_INIT(&ivec[0]->m_pkthdr.tags); + } + *ocount = nhbufs; + return (0); +m_getfail: + for (i = 0; i < nhbufs; i++) + if ((ovec[i].me_m->m_flags & M_EXT) == 0) + uma_zfree(zone_mbuf, ovec[i].me_m); + return (ENOMEM); + +} + +static __inline void +m_setiovec(struct mbuf_iovec *mi, struct mbuf *m, struct mbuf_ext *extvec, int *me_index, + int max_me_index) +{ + int idx = *me_index; + + mi->mi_len = m->m_len; + if (idx < max_me_index && extvec[idx].me_m == m) { + struct mbuf_ext *me = &extvec[idx]; + (*me_index)++; + mi->mi_base = me->me_base; + mi->mi_refcnt = me->me_refcnt; + mi->mi_offset = me->me_offset; + mi->mi_flags = me->me_flags; + } else if (m->m_flags & M_EXT) { + mi->mi_base = m->m_ext.ext_buf; + mi->mi_refcnt = m->m_ext.ref_cnt; + mi->mi_offset = + (m->m_data - m->m_ext.ext_buf); + mi->mi_flags = m->m_ext.ext_type; + } else { + KASSERT(m->m_len < 256, ("mbuf too large len=%d", + m->m_len)); + mi->mi_base = (uint8_t *)m; + mi->mi_refcnt = NULL; + mi->mi_offset = + (m->m_data - (caddr_t)m); + mi->mi_flags = EXT_MBUF; + } + DPRINTF("type=%d len=%d refcnt=%p cl=%p offset=0x%x\n", + mi->mi_flags, mi->mi_len, mi->mi_refcnt, mi->mi_base, + mi->mi_offset); +} + +int +_m_collapse(struct mbuf *m, int maxbufs, struct mbuf **mnew) +{ + struct mbuf *m0, *lmvec[MAX_BUFS]; + struct mbuf **mnext; + struct mbuf **vec = lmvec; + struct mbuf *mhead = NULL; + struct mbuf_vec *mv; + int err, i, j, max, len, nhbufs; + struct mbuf_ext dvec[MAX_HVEC]; + int hidx = 0, dvecidx; + + M_SANITY(m, 0); + if (maxbufs > MAX_BUFS) { + if ((vec = malloc(maxbufs * sizeof(struct mbuf *), + M_DEVBUF, M_NOWAIT)) == NULL) + return (ENOMEM); + } + + if ((err = m_vectorize(m, maxbufs, vec, &max)) != 0) + goto out; + if ((err = m_findmbufs(vec, max, dvec, MAX_HVEC, &nhbufs)) != 0) + goto out; + + KASSERT(max > 0, ("invalid mbuf count")); + KASSERT(nhbufs > 0, ("invalid header mbuf count")); + + mhead = m0 = dvec[0].me_m; + + DPRINTF("nbufs=%d nhbufs=%d\n", max, nhbufs); + for (hidx = dvecidx = i = 0, mnext = NULL; i < max; hidx++) { + m0 = dvec[hidx].me_m; + m0->m_flags &= ~M_EXT; + m0->m_flags |= M_IOVEC; + + if (mnext) + *mnext = m0; + + mv = mtomv(m0); + len = mv->mv_first = 0; + for (j = 0; j < MAX_MBUF_IOV && i < max; j++, i++) { + struct mbuf_iovec *mi = &mv->mv_vec[j]; + + m_setiovec(mi, vec[i], dvec, &dvecidx, nhbufs); + len += mi->mi_len; + } + m0->m_data = mv->mv_vec[0].mi_base + mv->mv_vec[0].mi_offset; + mv->mv_count = j; + m0->m_len = len; + mnext = &m0->m_next; + DPRINTF("count=%d len=%d\n", j, len); + } + + /* + * Terminate chain + */ + m0->m_next = NULL; + + /* + * Free all mbufs not used by the mbuf iovec chain + */ + for (i = 0; i < max; i++) + if (vec[i]->m_flags & M_EXT) { + vec[i]->m_flags &= ~M_EXT; + collapse_free++; + uma_zfree(zone_mbuf, vec[i]); + } + + *mnew = mhead; +out: + if (vec != lmvec) + free(vec, M_DEVBUF); + return (err); +} + +void +mb_free_vec(struct mbuf *m) +{ + struct mbuf_vec *mv; + int i; + + KASSERT((m->m_flags & (M_EXT|M_IOVEC)) == M_IOVEC, + ("%s: M_EXT set", __func__)); + + mv = mtomv(m); + KASSERT(mv->mv_count <= MAX_MBUF_IOV, + ("%s: mi_count too large %d", __func__, mv->mv_count)); + + DPRINTF("count=%d len=%d\n", mv->mv_count, m->m_len); + for (i = mv->mv_first; i < mv->mv_count; i++) { + uma_zone_t zone = NULL; + volatile int *refcnt = mv->mv_vec[i].mi_refcnt; + int type = mbuf_vec_get_type(mv, i); + void *cl = mv->mv_vec[i].mi_base; + + if ((type != EXT_MBUF) && *refcnt != 1 && + atomic_fetchadd_int(refcnt, -1) != 1) + continue; + + DPRINTF("freeing idx=%d refcnt=%p type=%d cl=%p\n", i, refcnt, type, cl); + switch (type) { + case EXT_MBUF: + mb_free_vec_free++; + case EXT_CLUSTER: + case EXT_JUMBOP: + case EXT_JUMBO9: + case EXT_JUMBO16: + zone = m_getzonefromtype(type); + uma_zfree(zone, cl); + continue; + case EXT_SFBUF: + *refcnt = 0; + uma_zfree(zone_ext_refcnt, __DEVOLATILE(u_int *, + refcnt)); +#ifdef __i386__ + sf_buf_mext(cl, mv->mv_vec[i].mi_args); +#else + /* + * Every architecture other than i386 uses a vm_page + * for an sf_buf (well ... sparc64 does but shouldn't) + */ + sf_buf_mext(cl, PHYS_TO_VM_PAGE(vtophys(cl))); +#endif + continue; + default: + KASSERT(m->m_ext.ext_type == 0, + ("%s: unknown ext_type", __func__)); + break; + } + } + /* + * Free this mbuf back to the mbuf zone with all iovec + * information purged. + */ + mb_free_vec_free++; + uma_zfree(zone_mbuf, m); +} + +#if (!defined(__sparc64__) && !defined(__sun4v__)) +#include + +#define BUS_DMA_COULD_BOUNCE BUS_DMA_BUS3 +#define BUS_DMA_MIN_ALLOC_COMP BUS_DMA_BUS4 + +struct bounce_zone { + STAILQ_ENTRY(bounce_zone) links; + STAILQ_HEAD(bp_list, bounce_page) bounce_page_list; + int total_bpages; + int free_bpages; + int reserved_bpages; + int active_bpages; + int total_bounced; + int total_deferred; + bus_size_t alignment; + bus_size_t boundary; + bus_addr_t lowaddr; + char zoneid[8]; + char lowaddrid[20]; + struct sysctl_ctx_list sysctl_tree; + struct sysctl_oid *sysctl_tree_top; +}; +struct bus_dma_tag { + bus_dma_tag_t parent; + bus_size_t alignment; + bus_size_t boundary; + bus_addr_t lowaddr; + bus_addr_t highaddr; + bus_dma_filter_t *filter; + void *filterarg; + bus_size_t maxsize; + u_int nsegments; + bus_size_t maxsegsz; + int flags; + int ref_count; + int map_count; + bus_dma_lock_t *lockfunc; + void *lockfuncarg; + bus_dma_segment_t *segments; + struct bounce_zone *bounce_zone; +}; + +struct bus_dmamap { + struct bp_list bpages; + int pagesneeded; + int pagesreserved; + bus_dma_tag_t dmat; + void *buf; /* unmapped buffer pointer */ + bus_size_t buflen; /* unmapped buffer length */ + bus_dmamap_callback_t *callback; + void *callback_arg; + STAILQ_ENTRY(bus_dmamap) links; +}; + +static struct bus_dmamap nobounce_dmamap; + +static __inline int +run_filter(bus_dma_tag_t dmat, bus_addr_t paddr) +{ + int retval; + + retval = 0; + + do { + if (((paddr > dmat->lowaddr && paddr <= dmat->highaddr) + || ((paddr & (dmat->alignment - 1)) != 0)) + && (dmat->filter == NULL + || (*dmat->filter)(dmat->filterarg, paddr) != 0)) + retval = 1; + + dmat = dmat->parent; + } while (retval == 0 && dmat != NULL); + return (retval); +} + +static __inline int +_bus_dmamap_load_buffer(bus_dma_tag_t dmat, + bus_dmamap_t map, + void *buf, bus_size_t buflen, + pmap_t pmap, + int flags, + bus_addr_t *lastaddrp, + bus_dma_segment_t *segs, + int *segp, + int first) +{ + bus_size_t sgsize; + bus_addr_t curaddr, lastaddr, baddr, bmask; + vm_offset_t vaddr; + int needbounce = 0; + int seg; + + if (map == NULL) + map = &nobounce_dmamap; + + /* Reserve Necessary Bounce Pages */ + if (map->pagesneeded != 0) + panic("don't support bounce pages"); + + vaddr = (vm_offset_t)buf; + lastaddr = *lastaddrp; + bmask = ~(dmat->boundary - 1); + + for (seg = *segp; buflen > 0 ; ) { + /* + * Get the physical address for this segment. + */ + if (pmap) + curaddr = pmap_extract(pmap, vaddr); + else + curaddr = pmap_kextract(vaddr); + + + /* + * Compute the segment size, and adjust counts. + */ + sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK); + if (buflen < sgsize) + sgsize = buflen; + + /* + * Make sure we don't cross any boundaries. + */ + if (dmat->boundary > 0) { + baddr = (curaddr + dmat->boundary) & bmask; + if (sgsize > (baddr - curaddr)) + sgsize = (baddr - curaddr); + } + + if (map->pagesneeded != 0 && run_filter(dmat, curaddr)) + panic("no bounce page support"); + + /* + * Insert chunk into a segment, coalescing with + * previous segment if possible. + */ + if (first) { + segs[seg].ds_addr = curaddr; + segs[seg].ds_len = sgsize; + first = 0; + } else { + if (needbounce == 0 && curaddr == lastaddr && + (segs[seg].ds_len + sgsize) <= dmat->maxsegsz && + (dmat->boundary == 0 || + (segs[seg].ds_addr & bmask) == (curaddr & bmask))) + segs[seg].ds_len += sgsize; + else { + if (++seg >= dmat->nsegments) + break; + segs[seg].ds_addr = curaddr; + segs[seg].ds_len = sgsize; + } + } + + lastaddr = curaddr + sgsize; + vaddr += sgsize; + buflen -= sgsize; + } + + *segp = seg; + *lastaddrp = lastaddr; + + /* + * Did we fit? + */ + return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */ +} + +int +bus_dmamap_load_mvec_sg(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m0, + bus_dma_segment_t *segs, int *nsegs, int flags) +{ + int error, i; + + M_ASSERTPKTHDR(m0); + + if ((m0->m_flags & M_IOVEC) == 0) + return (bus_dmamap_load_mbuf_sg(dmat, map, m0, segs, nsegs, flags)); + + flags |= BUS_DMA_NOWAIT; + *nsegs = 0; + error = 0; + if (m0->m_pkthdr.len <= dmat->maxsize) { + int first = 1; + bus_addr_t lastaddr = 0; + struct mbuf *m; + + for (m = m0; m != NULL && error == 0; m = m->m_next) { + struct mbuf_vec *mv; + int count, firstcl; + if (!(m->m_len > 0)) + continue; + + mv = mtomv(m); + count = mv->mv_count; + firstcl = mv->mv_first; + KASSERT(count <= MAX_MBUF_IOV, ("count=%d too large", count)); + for (i = firstcl; i < count && error == 0; i++) { + void *data = mv->mv_vec[i].mi_base + mv->mv_vec[i].mi_offset; + int len = mv->mv_vec[i].mi_len; + + if (len == 0) + continue; + DPRINTF("mapping data=%p len=%d\n", data, len); + error = _bus_dmamap_load_buffer(dmat, NULL, + data, len, NULL, flags, &lastaddr, + segs, nsegs, first); + DPRINTF("%d: addr=0x%jx len=%ju\n", i, + (uintmax_t)segs[i].ds_addr, (uintmax_t)segs[i].ds_len); + first = 0; + } + } + } else { + error = EINVAL; + } + + (*nsegs)++; + + CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d", + __func__, dmat, dmat->flags, error, *nsegs); + return (error); +} +#endif /* !__sparc64__ && !__sun4v__ */ Property changes on: stable/6/sys/dev/cxgb/sys/uipc_mvec.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/sys/mbufq.h =================================================================== --- stable/6/sys/dev/cxgb/sys/mbufq.h (nonexistent) +++ stable/6/sys/dev/cxgb/sys/mbufq.h (revision 170009) @@ -0,0 +1,86 @@ +/************************************************************************** + +Copyright (c) 2007, Chelsio Inc. +All rights reserved. + +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. Neither the name of the Chelsio Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$FreeBSD$ + +***************************************************************************/ + +struct mbuf_head { + struct mbuf *head; + struct mbuf *tail; + uint32_t qlen; + struct mtx lock; +}; + +static __inline void +mbufq_init(struct mbuf_head *l) +{ + l->head = l->tail = NULL; +} + +static __inline int +mbufq_empty(struct mbuf_head *l) +{ + return (l->head == NULL); +} + +static __inline int +mbufq_len(struct mbuf_head *l) +{ + return (l->qlen); +} + + +static __inline void +mbufq_tail(struct mbuf_head *l, struct mbuf *m) +{ + l->qlen++; + l->tail->m_nextpkt = m; + l->tail = m; +} + +static __inline struct mbuf * +mbufq_dequeue(struct mbuf_head *l) +{ + struct mbuf *m; + + m = l->head; + if (m) { + if (m == l->tail) + l->tail = NULL; + l->head = m->m_nextpkt; + l->qlen--; + } + + return (m); +} + +static __inline struct mbuf * +mbufq_peek(struct mbuf_head *l) +{ + return (l->head); +} Property changes on: stable/6/sys/dev/cxgb/sys/mbufq.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/t3fw-4.0.0.bin.gz.uu =================================================================== --- stable/6/sys/dev/cxgb/t3fw-4.0.0.bin.gz.uu (nonexistent) +++ stable/6/sys/dev/cxgb/t3fw-4.0.0.bin.gz.uu (revision 170009) @@ -0,0 +1,483 @@ +/************************************************************************** + +Copyright (c) 2007, Chelsio Inc. +All rights reserved. + +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. Neither the name of the Chelsio Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$FreeBSD$ + +***************************************************************************/ + +begin 644 t3fw-4.0.0.bin.gz +M'XL("'+5*D8``W0S9GB&6L +M%MMNT"(J;1%:JX9P4>NE"EYJ;S/*_IZU`];VG9G?>[[?^9WO?.>\T;7WVFNO +MR[/6>B[_9ZVU2<5XV`4,D\H9AE!A&):*/87U87@6QL?0W8%Q,*P78T,-1+&I +M4Z<+&"?LES@O2<7IO9\/A[L*2ZFF,>SB*.S;45C_**QF%+9Y%(9CM!J_ +M-(J#8\VJ.`);3(68QEE-XQXRC1MO&C-4EG'6 +MP`L-(0V421R')8Z3)HY3);*ON#@F9SI#F4X2QS:HY$S'`U%ZU`95)D3:0YE3 +M**$]C#D5SIRBK5^K@N9Q4C$%;VP05,V1X?#_S5$*_C1%T!B2SWOBK5$7X=6; +M$_HQ5'M+&--"6_%$/!4+NI^%-PG>WV1;IT^HUJFRU-F:96."VQYO^U,J)LM) +M;IA=9HV;R%LVYCN-D;0Q?CGS01R1L7@"'YK:H-(8D]BJWZ='<\U!;"W>4,:[ +M025CO(04^^_??__^5_Q`3KO^;8;3HZ3,C\2_?!U[-Q`N8,%;_F6NAGMO+'.B#>)9*I9&3L8%3+8"W2%D7<#D/ZE`>"!$0?PNND-0 +M7@.82+$4=I$"$D0'X_N$&9`'*7-@)`,\:?0'<)+$$=I+T%(A7@*ND-8 +M!'&4M@C"4HC/1W<(!1!':05#<0?:@`K', +MT8LJ[+F;D`YC_DH#Y/TRP$O--R&.[D=C[PYBF(1AD(T*@I`$(0/#(KH9!NO> +M\_&-R?#\,&J4!J,(6:SH?2J&+67IP(09+-/_A,K$0.!@`@S]Q\00E($\;$$2 +M*HZ"P$MB'S$2`I=MBX<%L[VR8CT8HD)`,I\P#-,#H9?/H-I^POA,1BP0I&+M +M*9\+KQB4>)//,(-<)AT>CT`8+A;+'+E9$^A';"I;MR`C&9I#.=C.,C>QK8'Q +MR8+@#%`M4JDL3`8S"#5##@PS8E`&1]3&,JU*U!-4.X8Q-SB8#)7=#&$]P]Q` +M:?^(93Z&,6VF&>9']'P/QO<,QOD'S,V)KECF6%JNR!C`$C+T5 +MPE*H9P/D`7D_!G6T\OJ`G#[4-+?=:4BV91B2WX;@A+`M`]J6(`RBL@RB>C*` +M9J@)Z.4P/QF2O=!EYD=X+X4X9DC>#^$C"NY0]J,:2)<9DC^$M,,9?3#N?0!J +M>J&=/G;F,Z!/QX`_CO7&WL,$<+\#M`C\\(-[%-#MA'`=LJII=NQ:C2KLA3,0 +M$"^!/GD!=-`+4R'88YGC1DBS`E^F0C-J%?8'&#WN82ASD/D'`_SXTJ3X1T/QF_" +M=;B#GGT<]+&J586-A?2'#D(^H,$,NE9O56':U2IL-(Q_]#(5%@ETAP*-L5`DPAL`+(SOUL4"$%`!Q]TT22P!P^#;>"U(1I&VP.TH+O]-__^9_Q` +M)GH#]Y8VX$70=\=6_T^I^+]___W[W_`'_'XE8!G_V>^?I#\1E_%?J3>@E_^P +M94@___3K]/\K`FN3ANX04D#_O?S`\__987B^$61"MGHH_CC<,AYX_@1N20\\ +M(U"A^LVS]#?/@M\\\W[SC/_FF8/PP%!\..V7WTNEG#^5XB^75EBV_>7XMVEW)Y2'J\T**@T.+B4 +MSR\5"$J%PE*"*!7].UJZH+`3[W:*H/T>)Z_!&?0W9_`!)_\#I^"@4WC(27SH +M%#7^:UI`)OZHCCI3*8]BN@GZ/[]'F$X=Y2Q1;`@+9>[A5ZREX=PX8IUUXPC" +M&B@_6X%U@&\\2`S5-_LEK/-7SY78E[]Z_@G[ZI?G_Y%?D`*S,3__#Q?[_\UO +MD32((\-3PBM.2=!*'(W1:@P?A3\]"GL*+172F.P&&7I#S&47#I4W1"-OD+13 +M\"'YSG:!+@>C"GCZW7=7;R?<&;H<#E4@T._^B7A&L7BGR)WA7NO.U.5PJ0*I +MX@;&3\(4`TSX``,1^0`3.L!L4-&I]%+EP*!B8)#*#&2X!U>6)EPQ\`\4Y[&# +MOTC*HZWR@1\S57]5XZG8HW12Z,#WZZSKK,M&)88/?!V4QT5)&+TT?.!ZPEP,_B]?\+5RX`K]>F(.!MU$]:$;6Z\*8@\\__9. +MU^@(GD$30VFB==I@?3R^*AH%?C"03VYG:IXR'=Z*#T=P7IU4'!"W&HB!!-;_VH[0_G5#YK +M5U**^`PJ,W+@B'E_%7UF32>>RBDW[??TM-M/N1)5)$UODI++9;O6Z,[G4`3Q +M]CIX;]*=SXX9.&A?KZ5&H_1[37255H*0.>-Y]B+Z8OOPAY<"NXH>[>F,&ZBK75SF+5&6E@@&\JH0^ +MKQS85K71M4$B2)(X9%09/_U'@>493+(/M\3C:Y8'4:/$5+SFG>5!^E$2?;QZ +M]?+Q^HDJ"\&A"$S\TWB!8(H]8ZUR8%/LP"9+SB2LQH&U%SQX!6+T^SWZW4K] +M`4^>=?GV"18UKW!,T6@Z2WA,U-#F>ETYD,=C9*ZE#D/56Y[ET0/OEKT)77&] +M0;B2@ATR`8-31!1<+6_QCM-Z%4G6R41U`EUMMO"X2+*#_ML&G.R4F`K,-_ME`!.-Z/"5DJVWP +M8;UQF2\I):2TH^:49)&4.&V-,R[;$!HZ,#,56Q@Y,/UH1LS`,\!'T()E=*:Y +M#T?TCT4-AEP<5;BGHCUWCV-?@UNW3?QV+K=X;_4>G1K7$TF$&]?%XY\_GYLJ +M7#!K_@:8]2G`,*F(86;0]:?K6S^@9V5YZ==YO&G\U;B]248OI=2XD<`$@NEI +MQ2VZN1B46[#A(_WHJB7;_MTMW/\]2Q/ +M_5%W.]N^_CFZB5X7.:"!>:F\4-XK8-"85_N>@(K +M2`T;D"T_-MUA,ZEY[\X5?BRBI\8.D*5]U;U9ADV77)=Y?EG05+ST/'VS^ANR +M.BF8D4G\>-,]N'@NZE1D1PWIEKWGIDJR&SX2G!%19=FG))`\G-A1,YR(Y`I( +M)%@2TYB7;,QSN+VHHX&I;D^_&F3NX)A/L_SW"Z$ENY=`;T33*F"@8,6.!1"X?3CZ^^K&?)C5C3@?V3_ +M3D-M54S_UB4'8-XQ4ZV'?MW>_I,%=$T+^8.L[B^Z_3ET$:$W8YEG^]3AE?V9,?T;!F+S1 +M])UJ3U5ECL%8%DJ?T)?)Z1JHM:Q*7R83LVS#6R.3#"#.&<#-95(88E899@M: +M1=0!4!:Y&@*-1W3_W*.TA1500VVFK@\G&W'#-C0CPHNC'/[JC'Q_;D]#8X5_ +ME9M7U&T^+88)(9F`0J@&PQG5_SQVBV<"U;((6Q6)FF!K$1S$J?NUT)Z,7-K1 +MW.!VTV_G!IF:.:86S*S&0S[&S:B:E(`.$:\+Z!"!7A5L4./S=_#F;^>2,`-DQ_>-TYZN6E,$T)5O.>VB__=938"]8:['W+=WJ35W*@^ZX$H7CZH9VURG' +MY77]K@ZJ)&S#EZ[.==?,UU4;OBJ4T0_1O5Y&(IGYEV7CZ(?X;PM)-\$?1_*Y +MDX^/KG(==M>/:7V$O8]OS41WR$/)%^>)JJK,9;(J5U6%KM*J[`^*:PD=Y^L!WUR';LYY@//&@['LNE*$WPVVMG-G5:6J+,+3'F +M%C5=$GG].)VE:XF+N'Y:WZ+1MT1&7O]*UQ*M;XFE6D8JKW?2FZF6>.7U#N)# +M7+`"IUJT@!]LS/0`OY`_(GZA78!/;,PXX!N=*AB,P'P7;_XFX!NP!*V%]Q6` +MZ&.D#PTJ,OSZ3O)#V;8Z8VV.CB!7(]-RW%B;;6\Z8IA;Q>J$3PUS/4T%=N]' +M.K5*F)%J4DT\QF#\21QBDG'6=6J[UU!#%3'Q^W\IJ\?C=POC$^3@Q"B+HZJ5-"M],WZ9)2.=VGO+YZ +M8R@]-1X4T?NN>GJ=8+9,D(*[O)1J8IZ5E\7AW1@O8*1"!T?0/Y[OEX+J(8J3 +MB/(D?IV4LDIII[9@D'=UO.3&!!$C)=Q2B1L7N_%B@PFZR>`Z=;S8I!#J%(KK +MRP6S<1ZH)CI+!=I*,%4FF(K3>7`I;2K6\]RRZJ/0(A05C$\*86=VFFYL):`?!BGG#:.XN9^[/X.Q)P(# +M?^_32MDY5864OPI/"?YC@2Q?2K>#R2=@^OO'IW5/M=W;,L2$+R`F_!6F)KEC +M<1AOX,8%[\(TEPPS)-@E>JF0F'Y\VV?)R%CR9N`I(F7B%BY+.H[%E'TB- +MZC@3P6L])O;@)L0#[1'7[II+JI9<@LI>TI=X[)M?T(<3].UC^X"=E=>^)P_+ +M=K3IF@$?2UF5]X2Q&=CY,8/:4C1/=&TL\*_=N\.B5DGJ66Z43/+^)"R>(&&$ +M(D8D$4PF'%RO0R"8O(.1_!T`)KZB'?%PNB^([ESWK)56ZX$?`"B"@"$EDJ@'/S)*8*`+CF`^$=TPS;X$AOIMPT^5_$IO1Y$\';U<0\P[IKW84+&5(95 +M*,HC[,E[Z=Y`S=!S(+!:6JSP*('(/8M9&MMS%47*PHB]>\UV;D%"OFY/&]E( +MBD1/52;DS3/$QPOKR/)Y,%#T;:%#0"4H;8-&L*?48:4K'*:#R.,BO3!9'\X' +MD`].0_H9H'DM3,W*3HBL<(07C]C!@/&$,@%ZSJD,;;!EP-L*MJ-^C>D"XE5IVOH6Y_AJP`>\-OO?^8#`S6G[/H:!7B15G+::"5XB@8$T +M1%][9KXK:/XF/!4OTZNU]F>YE%JS$C0A_AI0`,VW>EAUC5`D\2PK#YJPO-&K +M-D..E07CZ=OVYE7`\;0K;QR=0[@FN&+W%XC!30L/`84NKA.8:W/6'(&\4\V@ +MSL]/,JC->:^*=HT%796HCJ7"Q?;]4_GU#RKD8]7\Y81H.<$73!8."\%R1W1B +M.#\]PT*IXUW*Y?F)FG#%NTK'"$WD7?Z[)/YZ:EQX&,9%56A6<[B)$P3+"8U< +M$2,#).'0MJF!NGX#ND&Z+6J5P1CEC!&)E@#*Z\ +M)@?U"Y$-D7$:D;W^\\1PI38\/#Y\1/9\>MG&Z_2LBG['PJP%KANE`Z".Z0S^ +M-(YD*H>7,IY.XC\_?L0U;O9(_FS\W8B-%[.C^$BU`QS%%=\."G:`,LQKU^[/KW;OK]`>R*4V8+E[K=5`)8<>7%B84Z+;[30G!XC:2))\R@H0^F\)R +MBGXWA^4Y+>![>BWTAFY(`-*31'_A2.9Q@K/&TZFBG/'Y(RLNYD%ISW$`[QTEXHEA41/#8IB'W':\0K+D%'@$#H"`'N5FR2\_F3!X`TW$'6(<* +M%RQ?!_$K#CDP4*9LAT83S@=&L=W;"_)J#)>9P^4@K^&AOJ4IO":#/#3ZVW5F +MN9R^;93+TG;_F/>J[=Z'G1)]N-0^:RN=D3O?%5K1[[Z>N\"QT#W@NN%8H)=+ +M7=+")=NNQO$2R!XBHJ/=\2X$RI>!:XC#\GV +M>'0=V:``!&Z![G1V]+>C;/^X'=!YQ!)V3@/9Q&Z!OB1GR78P19F6DFS[S=7T +M>Z'?6DQJ))$BOXS7)A.WX:9GY'"MNA7S;:(_J>IFD6'3]YM^W/1#Y1WN`%YU +M&[E()=E=2PV'58:R;%^27AL,+:40'*1;V0F%B84YY3Z*(]LN5+/S*AZ>U],U +MCNWT+"I<:ALT(/-"S`ZLR0E`3D53^(\HO#M%Y1-T,ZRT5W@PL)IT(_I;;M2W +M7./I20LW1Z9O#\*2'9BS@(A1++XJ"L0#5[H]WTJ7T&OI7C2?=R2O<"1%XP4# +M4N%L#GTSJ'@\GY%*EN`ZN524G62L'"0+QP=E3W`8P#-$,^N7!OGQ$#\.?B:8 +MEZBK]P`(D@[\P&Z]6N5*RE;Q7%($-6?CE)H7]"F^<7*Q7N"054\552>951.A +M[I!)24$#4JCMS7D"O3J6T"M2,1RN/+#7>U#-GB-ZEC2"-T-,JI5$!%_]N^ZFS?)GW?)JJ=M7?6[-.L+; +M&/^2U+[F!QO35BAWR/84"JZ2NAEGSB$_R4L+,"5P-`?1U6&%6UMN8#UF,2X(=!W@[#&GU:AQL.R&8?CSG%TPK +ML#V(:?.M.H*W[`,8G[BTYLC(JT^;:JN6G`8Q'#36>NCO[/0/T5>?=(P'<&U? +MRS&&!X.X1EU-`H'>'7#1I,A%P[I-M=D>I;W]=--1B]I<-/^<2C(PUJ2.]?56 +M1G8^ZF,ZZ.XK3JLT6OYD54^0KFE7]5>2Y_ +M_JI=D"_[@"8 +M&.'SML&C8!&0/A#.L@T>A.Y7*,"2$HT"W?YA2(]=T^W/MCN_25#S/'35$<%\ +M&;)D3>QBBZ?`BM:VMWF6'WM4>>4?>6,*1]-W8J[\6+V^REELV+2Q?!/BOQ*R +M*BEX0.8IXRU&3-[4B[BTE%V0SM;O5NG1@I"3G3.$(ODIW*BTO4;;X-\Z)?;D +MK)@K'8'NTS<#*#?"$_;+`(PH?+4HS!%`N47S]A1V]:[P<;S7'1'Y(RKG>5Y% +MOMJNNH"C5IY0/!^<-1BEZOFV0048.%91"X.0`A7N!L<73.;?SJGR%2G"$-N] +M78'7@GL/8%=^VWWL^BK2G4UO4&BG00#>_HFO&\L +MR>88)Y,/`^/OO(W3WV9%[1'CK+L?7I^ +MV=^PHSQ>EE+0^AP&$`EL1D-0MRS$@<=@QYX\J-N>-->DSX1X7; +M1(T*-5LI[:@19NW$D)!I/%Y2@E6GC8_6RC$J/BI!^YA^5!B,!B&<+-Q)ZBJO +MZ^)CUUP>(:Y)QM1O7\9<>>QH.Q78,:C--#3A8@:WL$N)HHNCI*$6>?XBW]"J)GC^ +M^;WN>D=O_OD&AZMWQ:$@XWF.\1*['MG-KD?>^P!TZ#E5U)7)T5?B\]6Q5Y)$ +M3XOV46^OFT;WB@[A!H(C_'A\2,B4$Y3(]&E!F!498IX2KS+'#$4O`2U% +MB!8GRL&+E:,R;#QP96F!5KFNP/90L8TIH6\*AW8(#MO0"060*4H5//]=T#J. +M!((#ZCWMV4HDB$BNYC"'Z/J8R_<*MH1>_JQ811$9U!8QG1G2QJ%[$[>0)G44 +M%Y1-;9SJ,?H6EYO$'1Q/#$B16MV"DGH%8SB0%'MY0`3FLXT3>[D?WL50[%0(;,P"3"F2'7KX8<_FB1XM0.$W@EK%/8<.1\$`DQ,V)OMQA +MKHE!C+,E7)BKT&T)8Z\*]AJJJXF.OGPR4#=(;>Y6K@/JY12:8BZWB*,5(7Z. +MV(\JPUJFB[F)&)!S@`%2PUJJKM(+&" +M`1G[I!Z.:`(1[@`GKC:4+3:RV,C][BGH7A"#WA3O-&_1A!0IS%O4[%7EV,'> +M8W,-8@<'FA:S34=\I +M8`*"Q\@`S)JW:8XB&R`6/QG,]B3ZB<`(B@3$I,$A%;MU*DFAE[.XKBE:$&P#IY@ +MV(03DG`V(>+RJXA>%'FR<(NKKI@J2N#Z86Q(^E;(X`0<&`5LTI9UD)R[D3M` +M0EKLY3>XP"E^#OLF(2$T(4&1D!"6&(\[$@I-+AV`>EV"./3R0[K1[%8`UJT? +M[;'7G$&0_T/9>W6FVIR#,]XY`B_:=;5@0$^XI:7K@9=I;_1EP\%*4QG&^SW8 +M"IE9'4ULQ^V]3U)0T"'C.@26S&S)3)$E)SLA/OK$VY__P:")T6FB5[E!^K'C +MAT(OA_VKU8^.I<"K#LJB"5YQZRGZIO*RFFZ.ALO%?&G$Y1#1=-&!J>[Z*F^> +MJKK!];?J#V(N*]T'08-5O1_LQBL/>1JC+X^(NLQ%(/1#$MRH_4BO,:_2K4:T +MR/T*BQGXTY":1)'?_;+?#C*K'\U=D@E]O4.GAEZZ/N)R?-2E`;,JK#AAY*5O +MXU4BK1K`OM`T2[M@"F0Z5DZM_!FMRQU++]B?__Z2+K!W45@3K_X*YHCS-WZ"'ONNA[SLY=/WQ@_]^[Y]2V>D^C.9!]_D?02\XQ,^D^!XI%D<( +MKTZ`V7H[TZM3D2,OM9)NV2ZWN23GMJ"+* +MWA>`.MA?I$(;\PTF%;_\@^H#IK\1"5H^VHTMR3:VJ8QEV3;FSU^F:M$065#S +MM#=!&V9)X(><8?<"9FL7E$`7OU^P":YY\S=`MM$Z36Q:M5&G>G'!>GB,8S/T +M1EXZ:QZMUN]A2GI-H]7&T7$-?M,V]3O'@XRC-<9M:H/V1=O@3]`M>CWQ$REP +M3>`?X@IW`=59<6)IAAT#=YRLP;AP;=T)R-'-^6[`08CGM7D6_F]#G3EGZG +MGZ$T3%3236!81*T!0_9Y]*7949=F@]_Z=?+"YV.-+]U+/\9NBM<7D#'(EGTK +M"CP%KOE)>BA;-"U$,DU$/B=Z*IG7)B57XY6CHB[-:7"@/0@@R5KXA$/E>1@< +M4]?HXH1MC"4AN#`I]M)?5IZ>QETEHK^,NO2(QUM9#SUQOX_@,3`/4DH-A0E& +M%=]SP$$9_T:X/K"H2)V6+[^42#*R6D9S.[MA#3X@,O^0C3`FO)1?4K-OS+C-TI*(U8#.U-D;/`M>"I`KYJL_CZ/>-$Y7^Y*^34X*8?#GMK)#2?2;H +M3?]X>\_+^4E%4AN3&%AD[DXMER&`#\JD08P7KZ +M2.RE1XJ2]EE"BJ;Q^D7GVM,[<;W_)[K]S6O+J\-2L1AW#;5;BF4X7'(Z`[M9 +M4!WJVFQ/?4$'B,3/??/:=G[V_3A?-R5W<[$BK7K+^P.6W1)RIW;0.P^YIYZ29+\GR2&W?E]6DU.[\;W@Q7CZ'(YWL:>V +M<(J-V>9M2URM="MLS#*T=Z]7A>E4X8IO/@9OP>Z<8E2)>)\+#7U:X4=CQ8VD +MX1OMGCJ"F*;7AIVHR7^BF,J=4K\#6\]#^HJ1-.*&!'YQ@N0@N>+1$:S?"5(P +M@+9<:@-G26CU*8FXCH3J`G7L:80*[35+\Y\HI(JGU._%;O&P@5TFF93(NV(%390A.2BZ.*JKW].;6 +M._8WN-WU;^?*'5YHU56/#M;$XQJ"`^R#EK=W#A.!Q+.*]:5[K`:P1?39I#RI +MPUH95A'NE@GLL\I'T)L%@FE!.\:[%/12$_0@52R>;F]_(VU6@5FE6'T93\5' +M+'@"]/5SU4JDRSF6O/!\99%6IY(4R"A5B->MTTIV.^H=E#;$TLL]V.AE3+U\ +MRT7N;G]]FZF+,5WDIV#E\R?#[-Z@:W*U9I4DZIO%YE*N2162[S25\DD8`F>Y +M-I6#+7@B-_>[=Y"3%)M/Z37!J\N"*-6+]K69HCK27K-NY#-,*K(JC.R6[;I&[<\QM@6O`-O+66H$Y[5I8=&(PMLAW6//]N:!L^Z6 +M$PYR37YX96BU$GRV7G,X8,OT8F7:/F7A[8+0(GEEZ#O7H&P&.(.AA;(BN4-1 +MK;2H^`7*'8Q%R^])GC/82]0B7"A<."UMWYO1WUB!7<1M(N-G'`-=8J0W"!IE +MT(^H;Q;L;H0+V!/*M2"!?<4RLU9BT8:D8I). +M"4O/3:.>0-NIV^8-8&Y]\Q:?D5B164(4%H2`@VWAPA?/%9DIQBU(H:VHQ: +MDKHIT6OM`L'O*"U1]/W2@C^8 +M3X%_+)TI%D\3^,>9M.&VP3\F+_R(L# +M>DU,U,7K@2-?X`3MO@[6>2!2>/67,U_Y='G]?SJ;`2ZUX`I.(5\HUT=7*S][ +M%OG5:$RXUXOD]*S\T+,U;ABG`J5)SIDSV!YU\4L$N1IENQMU^[.+E<*=(MV! +M;-N]"RPV"8Z]>"/L8H=XABS\XD"A*D\C]N'X69)X1J1/P'65=UH?6E(%4RW> +M=6*;>>6%%\[6GQRK)S@`M(2[QHM$4TXV4/?NQ>>`:Q/UBYL5B8Q(W:\K.MI/2:`"+EM!VBN'M;37CJ#8)=I3^8;H.B! +M83\0/*V:`EZ,'-DX-AZXUGYQF*EF"7O``.N)@6?7'>D;4Z*U=O%>BTQ(K-\B/[5^V$D2LYE\HNF;-[H4UI +MR3,!0.C;E0`B*>(?%I7(H%(`P!(T$$:\='^_E4,@G2B9GL:40*7UC2+1-*C-7O_IZ@_6TDUD)5H* +M"LAWVQ_-5L&JO@A+N[+UWOWRV-\1C0%N!^B[(+,GL(6X8/WI`.X)@%:S2F14 +MA8D'A)8^+5"^K\WXC1:`K"_5_LE>(-+"$LD0EML(Q]X,X-CHBS$VQ@F@R7"8 +M"9!U\F^K/T`$!;9!5QU'0/A?]Q$9'Z`!T9>`0'LK*P.!==,E&U\Y\?S\4DU@ +MEQ/U(P+1+9C>FLGBU\U$^?`R"-UT3L5]%(>1.EXWM/;%%>MJ9*=KUFP2&(A[ +MH@J.88N,GF54*>@OQ6VX40MF=!NHA5\VM3E3[I]-$%#&96FS\A,)#JBN5,SX +M^0NYUJ&3E=C@4/7X][2U=2)HNS]GC7K7BDY6\J?!2-6?H%-!(1B&SE8>S;5& +M7CC#GJX\I*^MBK[PU9)M:,W5U.&)NM!A/S4:0!0Y*".O"]"1A!DX>R0A#<'- +M]:_$7OBILZ(=G&L"@-JI:F?LA2,'*\VW/=HRCOFZTOR#)VW6 +MRH,3Z,T`I,@]X\7B*?;VIV(OT#$7FLP_3%HX):;^&;09XD1'*HEA^62?`E>Z +M)NK"O@-\&FDD0*'(NEV(5MG6L\N&^/ME.C"L?DESQU\M'Y&,3KAJ`,+ +M6J?72K:XZ\&0AE`WN7DRK\-\DT_=X6X9,%]GS'?X,?2]#HMI9+$ +M7*BB'N):5"&%5LM#?.2L6BNUMD'O\,!6!XY$6NGU8)EM@V[::ALL?=<*XVL; +M?`TX+?+"VJ/.F`L9R@N9QL#"5R8Z*BMD<$O._:.RE4-'9=U[0$];:D,LV\3` +M.T=K`O#&-FCHE$1<6`HPAFZ*OK"4TL1@;>@XGKX#'=4B3N#ZTYG1%Q8(+X[: +MTX\.6N:UYW8TU.E/8RO=W*)35>WHK-87@7.6?;0W#YVOY$Z/NO"G?SJ,@?,N +M"01V\MG[>ZASF'G#O(S8[@&Q/NE]YX-KPU)]?RTWU6+EFHD?3:!3XG&+E@CQ +M`(.W'<\HRU%OV@9:G#.X,*`]`-]\@L/D8=E +MM2?`W=?-X+Y3L1J<_9@+X^S.OU8Y#V[R3C>4<=*2]Y^UGEMJ9$_M\K\(G-K] +M7=0%H_*"D6H9MBT!U1VM`(8Z^ROE3==8K`++,LR22C[@'7R,O(.`/A]R#O;8 +MF/>C+TB4%T+HS9B79YX;L^M$X;/']57W?SEQ!ROR(L.S/V9 +M/3"7!AZ:`O@"^MJUU)\$+D5]"GXE7_IOB&#/<:@4H`L1_%/,7ZNAO7J06FTP +M03S1NL5(8##V=/OP^C@GG-7Q*G3>D@>6H+D!#('>*J.(N!6;QZS:;#TY"S36 +MDHUV>_+3\TN?'%8IZN1LP`P&3:SR_/<` +M9'90@!A(\`,28O4),2MV1;?N1UEU1,9.G2XA1I<033MU";$=S@`M^CT_038; +MAL:)C6NUP8+!$\$LR2TKDT$0Z>),6BB5WK%K74!.KF/ +M@:_69D^>9U3C)#G]^)%4["N8E`4;06?N!UP.J#RM_9/(\QLMM55F0I!^;::N +MUF-W/AUU_N`F9\SY;(N:1Z\+G"&GLZO*JM=7;X*9-]5Z*&VPJ;G+A_2&9P2&Y,3?7[5T7KE^95#1]@""]E`\?!"=K&_ZF8!.I[; +M9CPM7NV6YG;#E+O]HOMKZ?#XRT/;RP9HX5#"?1YX&Z5V+!T:+^.RP+[UG'L; +M;(PHL#?#KOG>*P[P23#M/*W2IP9K"-5K6V$,OAIQ_H68\\^!VZI>L`6>OZ'^ +MC(24/:R;&NC$GX>P,7#L9[+MYRRU.?N?3D='+B/0>MSZT-+URO-/'ZQT.=-F +M/?,;??]*[/FI,>=_]U_4]R9HP2_;[C=V9),S1,;3V;K4X/GKP\VI8209"&>84$.M#5L!Z##[DZ)PB7G" +M'.:U0#EHJDO&/R&`WBSY`?I=8X&^W"R//I^L/!])'S':@RMON6Y2J43%=^CD +M]NV"5&2>?^!1J8J5:%G]H.'."+2(59N-OB?8EFTA.(B6>^/3N@_;!INP)EYD +M'V.8RT[Y&YEU?DP?2?6QJN.MX7EOK[B9V^[H:'#K3F-OYW+S3U6T&_^,D[MQ +M(ZB.P36@-V@O*'".SX"^U^A#_D#?M[JY,5#5CC9D7][(A"J1JKE?)>V^Z:"+ +MFQL<+GK%ST&Z9HZN!8,ZZ?5#M3)_Z)1H6,)-M=FF+J5I6W;KBT@9@WUC9;:C +M?DAN@_7L9T,K]/?.816ZE;.BE^#(KGS#T7,_S!<1XC>]DKIXA5>45/%V][@ +MIH^;BJ@MV!KO`D,-GP`;\R&^['U<_]%/K5MLS!YTO$:-?_X%@#43@+5307\] +MQ$_%U,WH)_>>F +MCPU;,!OC@#X2]EDEB:,QL.``K*AP7BKV5VAX9%_QT9K8OJ*(/F?`CJ/%@ELX +M@)Q?%@L^JNS-^RCWDX:Z\H]6NH.H_2'4`3%2U$120%(?0&:]2-AT9T?>*BQ;X9<$`9M!&<\(; +MGH1%RH2W/.%]?VI:D7Y)5.9,+!70YZO6ZRJE:;-6''RDRTF?`E,H/!/PIXIC +M^IZ/[IN-]J2FQ/AOIO\X]%&,^`%_:OBS&+JFP*KL>R*]1=/P#^$]$=1Z*O%+ +M05F[!K"'1K9#8]%D2SX161+813IEW_A_FHZ4E,-*O[ZF"$1@/MU$MP,H!Y4V +MAYEC&TSIE("?170'_"R;C5DPA/O^4#_#H36H)'I`?8T&K>3@HUOJZ@'^A>@R +MN'D*K]N2P==E<;%*P[5Y8?7AQF&]P%*/->*Y+'F#Y);.]ML*4C^N+`Q.DU +ML:?K\4,!!SQ65YDQY(`_%$]D;#NQW;2R\^6SJ2?'Z=GOVH)WL"YN@;[_GF7; +MKWWPX87&I!X+I53Z=RSPJHOV<( +M*#*.X`C&C@\.GF)/G4Y!/3F3^/]!+NR+3+\\!.Q"[N^'#L,Z<2NY@]KJH(!4 +MM6:(5#ICV#E_';0`6CL'GAX?2-EN8^H`Q:S?JH?>Z5"!(3_]8453>Y"?%/]^ +MK.XC`3U+F,+ZZ8X`&0M/1:9_/>2GB]%W(;_RTQ,TT;;!+&@UY4$_'7W(,&Q+ +M64::PQ0-G5WH?5M76Q73^R:[X748(`?MM3L;*C;0WRE[EX'L4F48R*W$CYO* +M.*R9\1BL`M;2>([7@\IFSU1@.X9%QF25ZJPR@S44JMR*ZMV)7A>@0_IHW2'<>$QXBRW]!#_"]/_TXU]'M\@/V%`UY.Y<> +M^(;UM&K!<^3P,$U%J?.?G07]//AHKM:BDIA!M!D+.'0#]0-F;8C)R:V?X?53 +M3KZIE+O%014P5"E_SC]>6##K83HCTVY,E43VZHU:KB$U)%=ET/(!I;I5+GO` +M:V'WP`WE=JT]A%;1ITQV26`46;S*?9D4I\C">Q\2/R8KUA2HQ7^2;#.0$T7H +MO(N&/($;$SCV]K^&]H(T&G:?H6?I"8SWXGB"F++B?1MY'#^YPWCO7APX(_=Y +M-ND!GDTJ"%SSU,0YP4ID'MO*U18-?OP6FDG`]\*7QI/DE+0O#MF8P[3SL,FC +M]JC<&F$)6D19=>3K5&P+[VF<-)#!C0+16'GP`&FO64A!@SF3@A?B"_\.!Z6+?E0U"H&<30]LZ4WQ+ZSX1+=(ZLTQ^N*U<#=$`?6&>0 +M0QR>:V,*0(HRA$-.D]?&-'1*#H-,<6R,4?_93ZA((HRG>*(L7Z/LZL6NT`#RI]L/Z]QJM\JC0>ME@/=1N0M(W_7LQ4;QZ,W*GKWDX^!` +MQ$3W[$++]_Z`6XU@S[!;7=!>00>0E+O][=R0W%,@=^YVM+H?CX,/"_RV"^%S +MEL_D7=:#X])FO4>O/^.DPJ6IF/7S%\^I*B-\="%PGGCQK#G,G[IJCF_IK#FX +MG;75F\VU&!CKD)!IYG#IN53[YF<#]KJ&M==-A58QNXJB[&F-Z5EK4(F*52&- +M0K-7:_!JZ/4AC23=*ZH:G__Z#J8B5N1(=L4L1U\G&7U)RIXW(WH."WX2":X0 +M1;&N:"ISD7"`LP8=?1MSME[9L]2B(MV1U2/!2ZR.$C`"ZG:.[C!O!4(4E.YV +MMK)GOGU]+)59%=LS;TD.)-HLMSVZ<)G]UHMZN?2T4Z<*JW,+V4W-U\KA=4%! +MC+AX6EJ3]^`BL?`5\5/)/+=4^#3N_O/PUIQE'I;V[-C?X/>_*'N>C^V9C?3? +MK_%[--+CYWZ#WPNB37(IU;Q(I^*'.,@]@>;YQ5:J9='*']"W4M$]CQK!ZK?) +M]K3IFM%*Q"&1KB6;/6/X:RS_KHTI[)0`AA).LPW.KG]:V:.75,B$$0IW3$3/ +MTL"@4>%B&+.8GD6`?6R#)RLCZ`_UX:'LV;RE!1%Z>:AM,([V1O0`C(V)Z8DJ +MC$(*JB1SK[]HY-"WJSUA0]^NEOWS;UP!.]UYM%W9(QC9(QSRY=CU +M>'3NHN>![Z_!G?OE:\L@74>([K08'$WVI`;Z%.@>](Q^+ZK[[Y2:5WF3IMW? +M!9;0(KOON&X%3@,:M6&!XX"VP6EH,9#.*HR)[1GCDE5+[>W_X4\]N$B_2%D< +MHULM6/SSXSWM9P*[B>Q>8@]M;%.=68H2%)!`OST=W +M?^[J=5^H[*-IHY;_P/01M?C]^3.'RT%5^Y,,X0K:.V=PKT&N,,OEMGO706<$ +MWL$;7]*_/L%^6/H&B/!2<5P_6X.VCAIC13LYZRHOQG0R<'#&/#[ +MZ.Z=([N_.U;MJ!?NY`KS..7UAE/XVQDCHKJW1G%J.Z+,=V;(KI+3>.2FR-(]GWG"C)]Q]+330*\7H$_[\0=WLN(."T1U40;"GA+\4GFHY?68#=)J +MV9=+0[N/YH7EA^IKL/B)/:(Z*E^:$OQ\ODRTBRH(-X3S#"U1R"&R8F\<#RM0 +MZ)-5AD=49!V5$CR=/D7XQYE^1S5,W3][_YBW)IS;O[Q87A3N"&L8\#I<857A +MZ5=KANP:VK5$8QG9_0=H)X63QZY,SV%^'MG]/'U4Y\5;=Y.5.$G.-/X-)]%8 +MQ70GQW9O@L%!*&3X"]'`?$5U3QO9_11[\#13UX3_=5\PO$*3*>]V1G>7*+M+ +MQ'LXI@.9`A='()A)?8J#7)1\KFE[:&@^Z8MT[3CNP5H +M&3LWJCN+?`P8S-#\O;[YCFDROFKSXPUUYN8?]2UWUNP,TC?_`)%5.PW'JL$Z +MP!-%<(A=..:(%#JX^I;OX242\[F9`H9C83\2%%\<%:]]<65CN*7Y!UWSCZ*] +MI,1-AKA)H4[16IZ*?4\<)`64(BWC.ETB/(L+A3-UO\/9CN91!DWP2B_K?6EB +MT[ZP!W@_N$U@:LY9\C40>PJD(+I[HMW9JNP65ZUW.2TJ8N,&,3KBX5"A58-2 +MBXI?4:8OXULV$O'L^0M3,[C=*E-+=F!M,%#EMC9]-`A_5+0(A'I*ZKY#4Y3DU +M7480GO*N0-U$5#=/[O]'J'\'[0COQK11TC#_]K-)^I>68C4YY5+,F>=6ZN9B +MB_-F2JIE>)60>X\P]V)!C%`R(.,-$/J.189;%-%*BAQC\WN];KC5Y0D:2>J6 +MKJ+7T+%8?WJ1Z`0IJ!OKZ!,*I\"=<)"NOEUU^M.+O>4`[,I3A]=$%/X+T)4P +M__E0O\^1O'/,B:(YS!F:UH^^8QC]?64R,!DU^@?SZ!]%^TC!`"D9($/,BE0, +M3"7Q<2#*+TH*9*9G1?O?.K;$=`J/];\%0[]J\V1J](_&6DR_FZV=E;I!^ +M]`\06;7S]Q#!U#S$Y"F1P<#D8[^'=&1];F<*W!S=#T.3!\H3\..JG=&!OQ$` +M;Z$4]F)D"!;_;6*I+8#K4?)9@0I(4WS(0/N*C[`Z:#-PYG^T]^1">AI +MZ/_3]"@>):361EO^SKR6M3K:7TZ:97O]^:?RVF/\4U=M?M3165#34%?>OC*7 +MF]=1WKYJY_-Y'4"(KP:;$"EQ<\M/Z4^S?\+@?*;0P0%6HX8URS#]V@#]AO.( +M_@Y:/QG''HN4^+GB1H[ATI`W?4K200]#=D2?SZKPJ^D,L-'`]#K$]#PWH2M! +MZT+.P+I0@/X8_TC2(-O[(5HS(::PA_*SC;79]"@[_4ZT_U6%7[;G(]258\RJ +MS8\%>H+V5G)Y>1UP_U_2EV#DH-#M\58^9<4BNJX+GI(+'025.;4K692!C>RZ +M1CXCVU6GJ\T2_E&DVY85Z%>@3$17+Y2!D@\4B.KJ)J?+=C?J]V<1+XKT![)8 +M&TBO)?6RJ*XS([N^V@UUY>PR&.J3N&X!.K#KB'Q[+5]3FX6/%@WO+00;",RN +M2@WWQV=+0[N.:<&%_5!@V#-HV&_6[QD414R>O^')^24S%ZQ/2<5FHCW7QY#/ +MJJ_5F^K7$\3OC?5._>[;`O;;K#>//V:L+S'5;P@\KCQN-NBQ@V.T,SAO/+T3 +M&#+]DVEA?K&-:81JXA^Y8F/VQQ-V&[/;1!#"K\;;F/>6OB(**E)8'N/$WUEO +MON-C8FDX\">:"*%&BZ>R5%V]7,UN"9''E@O5]'U='*.=$17 +MP;'_*)4INO)3L5'^FM37BTZ20%%4ESNFZWWR,!7=]?[>MLI3-B:>':_8KIDQ7;KTC?Z7C&9P-[M;=]3O8>Y:I#--5\D1ZM$VWD_0X/>M- +M9;BIC`LPU<:4C>P:93R0@18^#JP=QB^3Y5TZ760S19@BNQ)&=A6&=A5!AKC1 +M)B1AP(DUF$B4)')-H.)C%M2"9.?,86:%=:V-[)II>.0*\0@GAL:X59C4=WO<8;P,6/4>9XW/0"EL+]FLVSFME0@FDUI2\F@H.#=+^U]>=\K.V9K +MG]]E>GZW]LXUTYWKVHL&TT6CZ>@B[=&_V!@3&@<]8340W#G,,_*N*48*>$>WK-ION'KEEN8123>U*[:41]O9/C]0" +M0&<>#=PF-.^CO13D*M'EH%Q:5/8A9#V(SI\\TGZ<'C::[S<446[6E942L +M[[J9C9M:1APYW909US(D4/IFN:B.PW9>WR)O>H]^/T`.U8)V&PS#VP8"&W,PVA,Y+Y+H5VRZ"Y99)<\IDL>U26GD^CG1_K.A_L\%"%(0-A@ +M3(_JC9-XL(8OB)U*[5NQ]>\\;=%K$S+4KA_W_K3GYSI&4Y81EYWE +M'1._K$"[+%^S+$];5FTIJ[*4>;P6^F>E[W:$[W:,[W:4[^-(WVUR$372=XL4 +M!^\X4W=NEZ^J7KRS^R]W:+W=IOZPS;+MDV/:-=MOE81NB +M]/6P.BXTPM>87ARZ6"!"`^T9U->&IN_CV9C^3HG<]W6D;[;2]SZ;\3':*S@X +M@=JBA*?,>+MJ*5T_PK]SVDH_7U`3[2NJE$;Y&A?KII^K?_]` +M\%_P-_X2IO0YWCK)RY>*_D(*#E*ZWP]N=U!E'%]-A"]S)9N^BQ0>HKR[]I2% +MN`02MX`J\U#HSQMEQ)55Q]GSX^T%$;Z22-\8H^>>L5D,AH:,4K1^@71*L+<1 +MQ8_8F-OHG)S9'&_9JK5LU9BWQ@V/E=RW5.G+C/!M +MB/2],=+W>I1O:;1O"9T48KDQW([/L:WMAJ=YB[J5_C^&/*RPCPV:`XS>PXC07K7 +MMZJIW@1L_G?&?/I1-#>C<./I1X84Q\-X"MXY9_#(G$$/JNKW=^-_,,T9_-/0 +M/G^H;W&6-7[*LVTOQ$7.>.]W\4\^FS6KZ?%ECW"/64Y)@IX@2D9[G]@PZQ=L +MI/3]V9$1G_GLB>1W9]4OG[=;:ZV,6P_4,R%3&GKN'%O/@Q5T6CECV/)CUF-K.6G2I]?B:=*I:[E?Q`[7)ARN +M[?8#M<6CVB[_NC9:NIZ['E^/:OW2RGD"4HL*&_!%H:OO'_J>SH +M8YJXXN_>NZO8WO5:H!WE\WK79+^!$R9^+`;/MC<>G>M5`_ +M!NJ:IKGW>[_/=[][O_>N=[_?"_!7["7]G2F)UQ@T@.&85;("T4H1Q,1KD((` +M.2#MF+W!9&01YG0@0_WG0$HOF2FQ$>!>&TOA3U-[T[@'^GTA*A->.,.'0+MM +M?^J!-`[*1B#SE+P_D;4"(Y(SU'LF]7>.*!N>'?6#UM?;EL(QN;5VYW)-SH#< +M5K>K'J"GY8X,9LMIZO54]3J;9V9/LG,^3Q]RYYY(=YS(,/2Q(8'KG$^N*>DQ +M0/Q9E!&R'E6%%9%[L5Q@NF"^.UV]E*9>-.:8#;GZ'XI:^]^;;^WJ9YUL:-2F +M7LQ0O^=OEIPZU1,F/./@.7MS +M+G0?%?J/'9NSSW'^^H7!/@=AHF4%!1*A,5TZHF'.+`.38>FX:$Q?+X,0@^JI&`EI,=D +M!#T$(2HGWL=XF!?9&`935/H(TJ1^49[1M3R\(B2K'Q+)K>6[RKK*T2,RA_E$ +M931)G=TEA,S4PQ2G8%P&$S*X+XOZX\1`L]J2I+:@,3E9;2']6F92=8M>RTQZ +M%9N5,'T/.ZS(%WE`^0@H4?7%/+'+U&G6_%#U/NJ'F:J?OXG9V[C]!ANAB%?V +M):T9;^E-[$P1_XZP$3I+^0F$:)ZW\2+B;Q&W>^/+(6[(T'[VP&C?#2Y"\;ZS<13^++4OK3UTW;3_#!]^TG\SU6K>"MI']R5I8G^A"`O_>&IK +M2N^-O$1(]J4FM31#+6:1/.7:7(3.5,M/C1B'#/L'880R1JA,M<0?>3-+Z0=A +MFN=L!P:Y6S(O0DXD1OMA%1X0@4"C:HR@C2S\U^'O$%I%+F6(=-!%(3<-9!I" +M&ZR6":Z8EX4D%D4'&E;C-<([VYT)VYRV]YVE7K!7ZAWOQH?DPX6?N*`UF4H@ +M@75$&HG#I)5_M&%IP>@1W",?+3SF@CG)4,/YEF#FCXQWA`\.=U[NNB*E$[3\ +M$9.T(#Q%>:2H9V[./%WNO%E\`@U?3D8:V0Y"1E0B&\F0G6Q5B(+:V1?"?,@> +MYGG1+.91J`HW.@W2@@AI-K[T%T(>8@R0S$A,@7`)H4`(GQ7B%H7$\*3_694) +M9@S_"'0UU(`8<\9ED:_#9N/T@9GH)7WJ,VU?.2/_,Z7/8?MB +MJ[(W7G;,_EC^X^>I0691%A#I,U4@BTY!\8]%D:U*X_^K<691'$_A/VE#FL9S +M9@4L2M)3>3S,]TQX)5B5JKB*6=.,QQ7^"O^XMF-Q;9^P]^<_KX$Q*#)H@APP08E!]V4P-?<,.>`X)E&=],.)Z(%$P_MX:KT! +M1?UD3<#LXK+LXLKLXJKLXL6Q^GZQN!_[[P=N*IQ_56""-.VFFUGY1Q?<4<_S.L!\*SW +M;VAJ"`JOU`N5BRKJ7EV(A>R*MQLV-`OU_C5">>-F02X1<$EI84DI=@E+*VN% +M0HR+A>`F8:U_0\/6@M5-#;XU_J:&=9O\FYL*?,&-I<[UP8U^9[37N<6Y=LLJ +M5X%K3IZPG/0W$+):U]:M`L9%!1B3KY`OR*2!R<=$`U"P[/+J?P&C)[&/F'$` +!```` +` +end Property changes on: stable/6/sys/dev/cxgb/t3fw-4.0.0.bin.gz.uu ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/cxgb/ulp/toecore/toedev.h =================================================================== --- stable/6/sys/dev/cxgb/ulp/toecore/toedev.h (nonexistent) +++ stable/6/sys/dev/cxgb/ulp/toecore/toedev.h (revision 170009) @@ -0,0 +1,172 @@ + +/************************************************************************** + +Copyright (c) 2007, Chelsio Inc. +All rights reserved. + +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. Neither the name of the Chelsio Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$FreeBSD$ + +***************************************************************************/ + +#ifndef _OFFLOAD_DEV_H_ +#define _OFFLOAD_DEV_H_ + + +/* Parameter values for offload_get_phys_egress() */ +enum { + TOE_OPEN, + TOE_FAILOVER, +}; + +/* Parameter values for toe_failover() */ +enum { + TOE_ACTIVE_SLAVE, + TOE_LINK_DOWN, + TOE_LINK_UP, + TOE_RELEASE, + TOE_RELEASE_ALL, +}; + + +#define TOENAMSIZ 16 + +/* belongs in linux/netdevice.h */ +#define NETIF_F_TCPIP_OFFLOAD (1 << 15) + +/* Get the toedev associated with a ifnet */ +#define TOEDEV(netdev) (*(struct toedev **)&(netdev)->if_softc) + +/* offload type ids */ +enum { + TOE_ID_CHELSIO_T1 = 1, + TOE_ID_CHELSIO_T1C, + TOE_ID_CHELSIO_T2, + TOE_ID_CHELSIO_T3, + TOE_ID_CHELSIO_T3B, +}; + +struct offload_id { + unsigned int id; + unsigned long data; +}; + +struct ifnet; +struct rt_entry; +struct tom_info; +struct sysctl_oid; +struct socket; +struct mbuf; + +enum toetype { + T3A = 0, + T3B +}; + +struct toedev { + char name[TOENAMSIZ]; /* TOE device name */ + enum toetype type; + struct adapter *adapter; + unsigned int ttid; /* TOE type id */ + unsigned long flags; /* device flags */ + unsigned int mtu; /* max size of TX offloaded data */ + unsigned int nconn; /* max # of offloaded connections */ + struct ifnet *lldev; /* LL device associated with TOE messages */ + const struct tom_info *offload_mod; /* attached TCP offload module */ + struct sysctl_oid *sysctl_root; /* root of proc dir for this TOE */ + TAILQ_ENTRY(toedev) ofld_entry; /* for list linking */ + int (*open)(struct toedev *dev); + int (*close)(struct toedev *dev); + int (*can_offload)(struct toedev *dev, struct socket *so); + int (*connect)(struct toedev *dev, struct socket *so, + struct ifnet *egress_ifp); + int (*send)(struct toedev *dev, struct mbuf *m); + int (*recv)(struct toedev *dev, struct mbuf **m, int n); + int (*ctl)(struct toedev *dev, unsigned int req, void *data); + void (*neigh_update)(struct toedev *dev, struct rtentry *neigh); + void (*failover)(struct toedev *dev, struct ifnet *bond_ifp, + struct ifnet *ndev, int event); + void *priv; /* driver private data */ + void *l2opt; /* optional layer 2 data */ + void *l3opt; /* optional layer 3 data */ + void *l4opt; /* optional layer 4 data */ + void *ulp; /* ulp stuff */ +}; + +struct tom_info { + int (*attach)(struct toedev *dev, const struct offload_id *entry); + int (*detach)(struct toedev *dev); + const char *name; + const struct offload_id *id_table; + TAILQ_ENTRY(tom_info) entry; +}; + +static inline void init_offload_dev(struct toedev *dev) +{ + +} + +extern int register_tom(struct tom_info *t); +extern int unregister_tom(struct tom_info *t); +extern int register_toedev(struct toedev *dev, const char *name); +extern int unregister_toedev(struct toedev *dev); +extern int activate_offload(struct toedev *dev); +extern int toe_send(struct toedev *dev, struct mbuf *m); +extern struct ifnet *offload_get_phys_egress(struct ifnet *dev, + struct socket *so, + int context); + +#if defined(CONFIG_TCP_OFFLOAD_MODULE) +static inline int toe_receive_mbuf(struct toedev *dev, struct mbuf **m, + int n) +{ + return dev->recv(dev, m, n); +} + +extern int prepare_tcp_for_offload(void); +extern void restore_tcp_to_nonoffload(void); +#elif defined(CONFIG_TCP_OFFLOAD) +extern int toe_receive_mbuf(struct toedev *dev, struct mbuf **m, int n); +#endif + +#if defined(CONFIG_TCP_OFFLOAD) || \ + (defined(CONFIG_TCP_OFFLOAD_MODULE) && defined(MODULE)) +extern void toe_neigh_update(struct rtentry *neigh); +extern void toe_failover(struct ifnet *bond_ifp, + struct ifnet *fail_ifp, int event); +extern int toe_enslave(struct ifnet *bond_ifp, + struct ifnet *slave_ifp); +#else +static inline void toe_neigh_update(struct ifnet *neigh) {} +static inline void toe_failover(struct ifnet *bond_ifp, + struct ifnet *fail_ifp, int event) +{} +static inline int toe_enslave(struct ifnet *bond_ifp, + struct ifnet *slave_ifp) +{ + return 0; +} +#endif /* CONFIG_TCP_OFFLOAD */ + +#endif /* _OFFLOAD_DEV_H_ */ Property changes on: stable/6/sys/dev/cxgb/ulp/toecore/toedev.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/mxge/eth_z8e.dat.gz.uu =================================================================== --- stable/6/sys/dev/mxge/eth_z8e.dat.gz.uu (nonexistent) +++ stable/6/sys/dev/mxge/eth_z8e.dat.gz.uu (revision 170009) @@ -0,0 +1,1083 @@ +/******************************************************************************* + +Copyright (c) 2006, Myricom Inc. +All rights reserved. + +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. Neither the name of the Myricom Inc, nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$FreeBSD$ +***************************************************************************/ + +begin 644 eth_z8e.dat.gz +M'XL("%364````V5T:%]Z.&4N9&%T`.R]?WA4Q?4_/KO9P"9&LF+$!5$7Q795 +MU%BQY=T'-`IHU$#0TC9"(($$"!`Q0L1`0S8LV`8*)"K0*(&D%5ML4;'%]T/[ +M4%U,M%&3;.P;V]1&77E'FM*H6[*0!79WOJ\S<^_NW9N[0=KW\WR^?S3/,[E[ +M9\Z<.>?,F3-G?MP9QOZ-/_.2NG\G^W_^_O/WG[___/WG[S]___G[?_-WTFQA +M;Z(7'W!;[3[VS[*?/,-#B#;YF-5.3R6PIQ%OKF/6578><8UF%KYYSH&(FS-O +M19`YQC#7,6:M")@9V[:665UE/.BMZ&,=_C[F\O->;^@4JSK%K-[0YZSJ,69O +MJ?R,K1\%?!F,N7?RH(H[8N;,-9;R]K".AA[F:HC/Z[J2V=OQSMTLO;R>1UH< +M(19*G7.@\@5F<8UCYO9B#W-\C6A)R2):-BYCUN<7>\Q\\\(#+Y\-FC><8)9F +M^S36;.]FWOHIS!L(L6W+6%IS8#+S;HPPPNV@U5^2^U.WI=>P4R.M2PY/<1,X&%!(^*NS1!Y#OC8IBV4AU\^]Y^1 +MR^?Z(S^9>Y+_9&Y_^"=S`ZXUS!J^?.ZICC(A*UM'&6059M9UGS/;_,=01Z&/ +MV+JES!Y)65#B#7W`YEFV9- +MO6_Z39/N8C=-93D(-]W';GH0OQYD-SV$QT,:O0#/:7QUA('7`#^7S1Q5)+NT +META_((-`OSO5"OXM`^Y4XK^.>'%<0S"IW:O%VP/E8VGY-N1QU'UJ?IGWHDQ=WGQ!LTQ/3:H0,+-5FBE_(&4Y4\J_ +M'#`CD%ZNXWL?:/53^8#Y&F#,J/<15.?-E=0>+]JK*_,PX*Z'3ID(/T]9Z"?= +M0KXDP';J8%\C.$I'GIL(-V!T_*=9T8XL`MI#ERD)E9\?M0R.5DO&9.ZXT8H=S`6(HW=)#EAG@8X9RSFEWB#>UEXZO9 +M.&]H"]I&#J6],9Z93'BZKJTVF[RA:>Q:9L%S,LM.Y@/>T$3`;63W/,D_*0@S +MV_U/`C'P!V<$[PW5(X\<^WG-LVU(C@1\OCFNYNX3/&R>^XLS+N)E[L\E;W-?H'BPFXS? +MK6&9]@?DFST@Z6OEUKE=8;<#.'DUR1?RK@;N5M(%;T6(\92Y7:"I%_G/GA,T +MY5O/_(!PYP\'GOP!*M>:;^4U^79H4"AW#0\6K&'FW)`IY*W8*&RAMZ(4LF,C +M4<8;/"7?2N7P3?EVX+H,.(J)EK-N9L.[\ZR@*S\[8LW/NX65F;2Z)&0O=.@E +MBK.1WI#.D+X4A*PL4I,/>QM@T$,;E1NNR9_447:0S0SP4$?9#M81V,MFEOE- +M'8$M;$8?]\TOLU2W]WW`.LH"2,MA86O^H;1"%P?\&Z`U,K^LC-WW)/=WE+40 +M#A?BPJ&:?&?(FM]Z=AA#G@:2V[ET4R@IO:(ZN2/P`FLO.2ATM[TO!R'`9IX* +MI<\K82,`]R7I,!]1S7(^B_"9I\[QML(M@-]"LLBFOF3&Y_V('^#M)2^QML*# +M@/.DAS;E.\.;\B=%4O+S(IOR2\(I^8="*?FM?/.\29!=BI!_ZKQOR?J>]TW$ +MC95U,B^;U\PK.;=I7IZWHACU."^;=)]OFE<"^!(%OH+T`S`U"/5\\_S?G:&V +M;)U_B-<4V+T5#:*]>BM>@'R9@]JKMZ*<%:P94XWW:IX"N$T%=M(5GKI@#EH[ +M='5!WKE-"RH04!>4_P5%QQ?D:>T)8Q:8_&0K8\,0AEM5^S+@OF2"C_W23G8) +MOR?A=Y_R>YJ//2/[*-B:LBIVV0EV23",=H[?:?@=RKW^#1ZIV7$(LAX@&\#= +M$V'76=;`0(F5#SC3>&K15?RJJ1_"[TS^ +MA#UUVVHV(3W`K(WN2%[Z>AM#?Y%!O[E[A[_Q-+.0_Q!)07D5Z9RO>8"/3OKLFB[U5P0Q]L@'WR"C_"=)S5)DD2"]5 +MY93`5[)0^PJO1E^XC)$L[*X0_U_(L;HJS*[,77,U]_9QC[?B<]:(]*I*'N&; +MBBS]JWEH-_K.2$H1E='I8]>VR;K(S]Y*?:JUR`)K@,LG;$'&.*LV@<91M"EG&RR@O*;NV'S^=:Q\9"ME><8)<^XJK@Q[G[ +M4LOS:X/H"R^M\+&-"66%_"61T[RW=G54#I_FKHEP^NVM"+-&Q!/?P.-1^4TL +MTR*[](DR'!Q^NN>J$/T>1Z2C'>;UHSV2CPA_(P"?/CC@SH#_,Z9,]:V\H>G4 +M-O,2X,^(X;_L/[S"MEY7H\E3H\M0,IO6R!EV>_;H\A]5WZ5^R3,1UZO+X +M='G\FO=+!]RCHOQ374=0#\!#>@L=)+M,?L`IMJJ2!Z$#V8#/5/&KNB/@I?[T +M2OT9-3L*`W>FQ1%@D&4OZK3/&YC.,*XAF=:33JEEIE=*O:'QBUJF#[I"?8)& +M/WNH+*6,0VH9P-U#^B/&;,"OY,N3& +M[=*:;W>=85>Y(OP8='4']#Z)GN`A*3<(?JU+\FB\ZZW$V#1%P%X*V![`/*G` +MTM.B/(?3$_[*9:"YP4\^D'4Y4]\YWO&^>=JU]_9I?!]!LAN8$PXVEM]#'+BU8V`A?T<*^V(W3J4 +M'4'=U(->\W%F?YSJX!BS%Q(_"";$/^RD0V[##*WNAC7ZN3XR7[+,*5@(8:&D>3'0&NSQU5)J+CB+`Y +MP-5HEGUD!+3N<4=*T!^61#8OS1:_E32B(X+R#?%';=#H9Q4[5A&QSJM7[&\O +M>$TYQD;3NYF;1T]^.>(W'RE7NX#1]0:A&`$0HZN5]RV"A_YBC-M&UT=2()\: +MDM?H4MX_WJS^;OPGC2%'DXSJ$]9'E-8QW]706J.AU7J,C9FMT-H;3^N8V0CH +MX,9,0LA6WK4!_>,8.\K/D'5"<2B#9/%/9HV^BE]%?=0S9+^$/1I3I[-I\)/&[%7;EA5]Q./%$_0W:+_*5&_&ZNH#Q7:/L_LEN!@VN#%LI/ +M\S$R_YA`(ALAYC]JECL[&N"'0\KM#5GH<\\RHNT8NZ)2Z/"*LXQ^>RN_S;R` +MD3;QBH,M%7+^HZ7L).NG.EY)NG[%I\V5?J)KO\_\7:6=7.&C\OMKECOV$`_$ +M;V`2P<#^CSFD\KM-C)FOJ!F"5INT@V.M\7U6,FSGV+'QMG/LA'C;.79R?)]E +M1I\U=I8N3Z$N3YDF3YU2SD9=GNVZ/'LU[^!Q[$&U?OK%/-S8%LV[F>92$->E +MQL$V,_@<`=()!5]`IV.`O](:3\.5]G@:KG1&^TG@1WV]>9Q=)=H3S=>AO>72 +M.W#5`QYRN#)/AZ]$AT_;_]OP7J/AP99D9R;H2R_BF]1X\I=`;Z]+S(%FT5SK +MR&/LRJWD6RDXM?X/4_C2]7]7ZOJ_JYB&KT#Z>O9@>NBAA\GW`E]^\H'@"]A1 +MIHWF=X^7T]B[%WU;445"?4I=GDGM-F!F#N`O5/41O^'_7R%\7KYY21UWY=5# +MCN@_KE9LV/)IU!;1!_=>F\'2Y'S;54T:?0XXUM[)C[&K%]!<*/D4Y$$J[)<0LE`S]%IWI,L;"1#.+;+?6ACD[C?RT*=U&?L"5[>BS&KI +MERT,0A_/'F>.!7B>PW.ZBM-3)7#ZT%]^AW#IXH.H_[/IH>J\P6F.#.Y.>L@@ +M?@)LZED?E$8^XQDAU_CXD.M9]&%F)O)\IS)6'S)]W-CT +M>C9K<%GC)G+W\._XV+A2H2.Q^!Q)`V1A)EF`#^1+5(]J7TAR&'"/@__O*%5] +M<_C@?I*5DG9(EQ;4I!V-2UL7C>]3X\]3%Y#_-4]1GG@>K[$;U\$UF:B#<\9U +M<$V.L=Y<4\R?9D:X*E$OYZA>J!X&R_^:!E7^Y^$A?)Q=.SVF3]<^I +M6!;C)X/G+P?+8OQL51;HC]/YK@)^N;YYW=CX-GL= +MB\D;LD$>S?KGD&'H.KCNGXK\/XBGX[HR8_E?5Y-8_M?M-9;_=8=)_N#)'V^/ +MKSL:XPGE(0]W#[N4WK\J;VH`?E/C<_#OW:9Q_TK^1*%NC/&\(\:RS'$5^0E? +M>PVZTX7?&('E]K,Y;MUWH@VT^,9?MUEKC? +M^/I8\/_)8/W^^D29AVV@/'*]^.LO$(S4XZ]OI732>4V>$N#*B^GZUXL5'%J\ +M6]+]1CK]];T"7T#()V\/E;F:,>`X0W`4?ZU=J]-?/ZK"D8U18-OXKA)&WT&[>EZVF<_8F/73\YOBU=[XBU)90GVA*[Y$+: +M`GR%A&U/;5]?529H#T23\'7@=T\B&778&:V[NXZS&^ZC<;"4U8W).OYZC65U +M`YPUYC*6U0UCC65U`_P?YAK<+F[((;L/&FFMHNL+=L-N8?_["SCTSL#^WQ"S +M_V.H3=P@_"G>#_M?SX937Q#K`S(-^H`;#AOW`3<<->X#;NB3>GRC(UZ/;[3$ +MZOCZWO^C/J`6>/^"?KA:UL>$3?&TW%AA7!\WUJ$^:HWKX\;]QO5Q8POD7CNX +M/F[L_FK]\`1+?#]\8^"K]\,3LHSK8$*><1U,*)-U,&%O?!U,J(G5`63S;]9! +MA.I@-6L\SFY:@"?D?\LE.CJ"QO*_R0;Y-QK+_Z8)B?N*F[(I#7)NC$A_=N1W +M*GTZ?_:F4E56H.D5O(?TL%ME^68OF-0S@'0]TH"^MJ,]>.F'J6O>86G +MZ'7D9MJ/82"'F^U*GCQ!`_2G%D_/6IJ'O%GT?Z"#>X,ATH>\-,`'<@'F[X=\1Z`=()7L=#M]2U6S+C=>WF0$S7;MH>G^<6 +M6WR?-B'X?V0/W@#NSZ0MR/PO79D;C77QE@;P^89Q7=]RR+BN;X'_R][@41W, +M&JY+]QNWUTRK,;Y,AY1AYNQX&69.BLD0]/_?R,A[G-UZL931K?_4T;'/6$:9 +M'LC(:RRCS.[$[343[9]Y!]O,6VVNW6).0+2][X1@2]QJ.MG*6VG\Q]/'LEG> +M^A"MA=D(KJ!2S-.G;R0]O8;T]-8\V;]SEEX1A;6JL#J<&TF?53@Y%U&MLQVW +M[C.NMUL]QO5V:Y>LMV_8XNOM5G^\?D.N%U!WY0YNN!^+;YC;55;%KCK!OB'V +M_M'[B/4FVJ]AYAN6=Z7C=Z.;!VO-O+/6S3N]H9/DIUQZC'UCG:2/!Z4LOE%. +ML`/N;^Q7Y[@2E4?[<0CW4',KP!.`#++5^=\!]VT6=>T+OVU(.S3$>KI576,` +M[&0MG@TR;I:ZEH7?^>?!I=!SVT8=/=O1W\U6<#0-A8/J2>H7U>-MA\XW-T)S +M!L?9Q!=E>YJH\S]N"\JY+]G'S`_I]6VB/3U@I&\3,U5]@\]&>)?JTF=)O9NX +MKS]%ZT=/%&,'/E*V*Z3/TN6KB=?+VX)#S2-*WFZ_2_+V3=VX8.+1\_`6,.;M +M]K1XWKZI&RO=+OK__D%]Z.W35#_+<0WY3[?O$/Y68P$?[S#RMVXOC_I;$EZ. +M!1OA;S7I_=XLUAS2^URW'TQO,*2_3;%W3P^V=[M6+V@=&FQXXF5 +M2\L7.184%2U:M^L:W'9D5UU5,<)1.N:XXE:F0*Q^*6G8QMW2EL45]'DYAK01W/NKT=(WQ^UL4]U]"ZRJ3KB=";FZI9V3+:TS9I +M.G_=SQR+F?D8>^2^%F@KO7M[0\SA8M;C+/=3I)GX'1'$!90U^TG/4IQ8'W5_ +MJ[[A:69MV,F[=X]DEL:G:5UOTA:?^:%*LG5;$.]CCV037I1=`ALT!>./*14(U0BP.5.0?TH#`OS**2T(Z#>G`,\=J(\[()4[ +MD/\.V+0[D/<.Y+D#>>ZH1X#T[MB'@'QW'$;H1("??0?*OS,-P8DP#:$0`67> +MB7QW(L^=R'/G(00/`L;Q=R+?G?!3[D2_G`6ZLT!W%NC.`LU9D$@6RLP"C5G( +MEX4\6NT#CW1#?W>#Q;N2[&_GN!I]WPU^_&[*Y&^./NT'CW:!K*N"F@J:IP#L5 +M,IP*?J8"9BKHF@J>IH*VJ1C+305M4PD>N*>AQ4\#_#3@FP:X:9#7-.";!KJG +M@89I@)D.O--1_G24/QWE3T?YTP$_';BGH_SI@)\.?/<`[AZ4?P_PW8/R[T'Y +M]P#F'N"]!^7?@_+O`?Y[()][D.<>\'D/RKD';-"5C7%#-NH^&_60#;ALZ$DV +M>,U&.=F`S499V:B_;/"4#9[N0Y[[4._WH:S[0.=]R',?Y'_?003`W0>]NB_` +MV/V@ZW[`W@^^[@=]]Z.<^U'?]R//_:#I?M!S/^KL?N2Y'[0\`+UX`'`/3$:` +M3CT`.3P`V`?`QP/@XP'D>0"R>`!R>`!\/`#Z'D#>!U#7M"25`WW,0?X6`KQS@R0&>'.#(0?X+O+F@K95>#/!TW='!"_BKA!Q?B7. +M-#C.&86C9_^NDB3"H>97WOUJ7GKWP2;))[T[D6_R4>7ID\])=<2#RQ>EW8*R +M>NF=?BOET?=1?EV<:7"<4WQ'I8.3?!;RWBKPCG=+9)>D7Y53@C13XC0GTY8A +M^-;03/1JXOV:>(U13X:/"2/.PXKST[E +M>2!>/DY+K"RG7G^T<:;!<5KY./7ZH_#I--(?HS13XC15/DZ]_H@X8_DX=?J3 +M98G)QZG1GSN[E:=?>78J\E';_3"E+&KCPW3RT<:9!L=%Y:.%4^435/1@F(%\ +MC-),B=.$?*)E:.0CX@SDH\9KY'/7>$4^&CPDCZR0?-YE4][]JOTA6C1V8UC4 +M;OAD^U?M7&272//KTDR)TYS:?$%=/K.:5J78$))QE:2;ZMP'T="AX5>USXEXCFCKP8!O;;H//I1JUV+XI9TG_$@?+]_OSE*>-K7]*O#) +M.EK\2EG)!O+7INGEKTD;)']M/KW\_1JY)2>0OQ&,7OX&,''RC_)K(/\XG@WD +M'\=WO/RG=JMV(89?*_^I>^7[5(_RK(N7OU.O_TI93B/]UZ8ED+_32/^U^8:0 +MOS.1_AO!#"%_IY'^1_E-+']G(OV/XSM>_M/SX^7OU.G_=$7_IROZ/]VF^G"A +M*TJ&4_GTU-I/S;LI_EWZ4N=&EQ?&G3?!B':7U#57^4-$6N]RKRO+=:;;^J +M[=24'>=O:<[V<@3WVZ7IZZ]#AY^K6VT8@G`WE&^8J7 +MYWTVK2\9+\]L91R2'52>G3%Y.O7Z&>>?Q9?K--)/)=Y(GDXC_8SS%1/+TYE( +M/W7I>GDZC?0SRI.Q/)T&^GE_H=;WC)?G_9GR_?YLY>F(V3?G,&E+G,/B[5OT +MW13_KMJW:+K&OCF'&=NWN'B3<3S9-Z?6IOFG?G!I_\H$* +MY5FC/,L&VS?G,&/[)N(-[)L^WJF%-[!OSF%#VS?GL*'M6Z)T3;F#[-M@GG1I +M">P;R2ZGR=B^B;0R^9ZCR#.G<+!]BY:MLV_1;)0)X&]HUD-L-O;-]$FN+GS>A2GG*\#/YHSDC;#C7OIOAW +MV0XUZ=%V2'%&[5`7;S*.=S(5I]H.E?2X=JC$:=IAKH.>L;S$5RY3GF)<=XQ- +MRJ/Y^'YS.F\NF.M"%$!AP/VB-KCT@?2O2 +MS$RN+F#/9M@BS;!S%>S?2-Z3?!^L\;&= +M$^6W78]DTYE%Q]BL3CI#@%OGM+K.L#3Z!MQ3]2'Q/04R]8OS!,[\_^,\`9(S +MR@H`\#[1[HGP=ZYD$#YP'_/."N<7(H"&^4B?C_+GH_SYP#O_(`)@YZ/L@@P$\%<`G@H`5P`\!9!; +M`!UB+D*T*^(M!1!-Z*0$<1]*((=!3U,E8,N&+(IAAXBT%G,>19#)AB +M\%\,_HM!5_$!!-1S,>`7`?C_,4H?S'*7PSXQ<"]&.4O!OQBX%L"N"4H?PGP+4'Y2U#^$L`L`=XE*'\) +MRE^R#P$\+H%N+D$92X*,E0!W"?"6`&\)9%@">90@3PGRE(">$LBE!/56@GPE +MR%<"NI9"CDM1UM(L!)2UM`8!<$M!RU+@70:ZEX&>98!9!OU:AOI9AGI<-@L! +M="T#_#+@7H9Z6@;XY<"W''#+0?MRX%L.NI>#AN6`60Z\RU'^]^@)&I:#AN64 +M![27HHQ2)P+RE4+G2J'SIO&@=:F";>KXDW&<<[ +MM?!!#7R\;&;#%R:9%<`7AMSTLC9(-PV=[AS$3YSL=#S%R5#'5[PLO]\G8*(X +MM3+]_F'Y_OVCRG,_R16P5"?)NK)%G($\U7B]/)7X0?)4X?7R]"OR2$X@3WVZ +M7IZZ]#AYBC(-Y!GER4">4;[BY?GP9$6>"DZM/!].D^\/CY?/O%!,GDZ]?HHX +M8WDZC?13B3>2I]-(/Q5Y.!/IISX]@3R=1OHIRC26IS.1?D;YBI?GG"TQ>3IU +M^CDG7[[/*5>>.8H\R?X,TY4MX@SDJ<;KY:G$#Y*G"J^79U#1KV$)Y*E/U\M3 +MEQXG3U&F@3RC/!G(,\I7O#SG=BOR5'!JY3EWOWR?VZ(\&Q1Y"OX4^S%,QW=O +MU5%IIXCGR"=QZ7Y=NFGH=*<^?U"7WZQ-KU)L'M5+U=$H?>9(03P=58K],X`S +M?34X'5T:>9`LOXI,]'V:D5SBZVG>--4NQ\IRFK5E`49I#_,4>Y+?I]H3)4^R +M`6U^I=SD!/6E33>J+TVZ87UI\QO5EU\CW^0AZLL(SJB^#.`&U5=4'@GJ:Y!, +M#.IKD%SBZVM^0+53L;+T]37_H*RG^4I_.K\AOKZ<1NU+*=>9J'UITX>H+V>B +M]J7-?Y[Z<@[5OHS@SE-?SD3M*RJ/H>O+.53[&B27^/HJW!M?7TZ#]E58+.NI +M<*/R%/W+V4]HGCI&#[WK[;T29QH<%^]3*W"QOH1DKHD?U%_%IYD2I\7*H3B] +MGZW2K)>;2G>\K!9FTE/BT?87"ZWR?>%8^5R@^C/"EU1Q4+Y$_JF29NB?#DYS +MZO/%^=]Q:0E\_6^?+S\BP](N1>W*<\&K3]O3(LSD?X/\G\'IR62OS.1_@_R\1/+ +MUCF4_@_R]0?+WYE(_^-X-I:_,X'^+RXYI_/]X^6_6-'_Q8K^+QXK[;,SKK^@ +M]\'V6<29!L?I[;.`,[#/,M[8/D?3#.RSFJ:US\YXWUY#\V#[+.F.M\]+.J5] +M=NK\^27[Y/L291R_I,'8/CN');;/(BV!?=:G.?7Y$MAGI)W7/@N8\]CG1#`Z +M.@SMLS'/,?TSYEN;[F-+K8GM,Z67=$JYE_0IS\/&]CE*BX%]CM)A8)_U:8;R +M-[#/4?D/89^CLAW"/B>"&21_`_MLS+.!_!/89Y+OLIK$]EFDSY)R7U:J/"<+ +M^8-WFA/5VP0ESC0X+MXF*'"#;((:;V03-&F#;$(L+58.Q>EM@DJSWB:H=,?; +MA.4]])1XM#9AN6(+E@O=/,9FU='Z(:T]1=F5[`43=I/J1PU7WHE,VO2?D]I-WW!3/A=HJ?G7Z-EQ26):5EQ4V):5MPG +M:.EC)L<*=LD7[!%Q9J;?N(S'*6W`S2P#&Z*XM.7LT.;5E?/J$'C;SX/W\\1X +M'TVA=5C(*CUBCL'<'&+)$N8$P0C^^8:YG[K&B6\TQ?FKVY#N#?4P6A.>MH:Q +MFP-Q>1;H:1H14FD2Z>NU-"'-K$G;':O?1QV)ZI?6F[WTA=4XQIHMC-94^[9' +MOUM^%/K_2)[FG$.ZG^;JCD"`T1T%=,XB^+B\ZA0;2>>\TEG,!>%99GD725FF +MFC?1]]%4KE*F_QFQMEY6K.8AW'2N9`3Z)\Y)#O71&O7EKC54%I53R)1RZI&G +M9XARAN"O[.B%\\<4_AZSG8>_(!C97LU +MYYFBS:P\X&./;8^=!TK?JJ[\K*!)G*L@[H'2P,(F/S96[JE8J9SSL[)UVKKX +M<\82G4N,-MU+Y\GZ-RTIS@WQ".P?^HY5T/_RH^*_4DWC$1P&G[#KN)+65)<$$X3N.39W*OV1N\T +MD#29O*';F4*/1\6ME[.>KX6//EI>L'(1/9S7/7Y]JJXNTJCNN7F5C_:F/#-2 +M/9N@W*'NBZ&S)*'/=Y(N4YTC;?*1RG5B3\:G5F9.9(OH;(?^E*>H7DQ\T\X\ +MY"OWL56!Q.=0%K'PYN5UCC&,SJ$W_V9M%]V'9*K";[*K]!MT<*+C]9-=YN.L +MO)5O*H(?4.Y)2(/FC`DZ)Z"'@82$YXXLRY;W&3Q^>PN=HS6&_>@8>_Q6BYW9 +M\?L7^"W.CJ:T.)E?^7F3)8F;.>+K[B1(:O@P"TL?D6:] +M^JJQ]COOF#S)5559P2.A8#K7WB%1U_3=ZZOE>1F;ZIJ,:-WJYF5TOG$D=4>G +MZW%F?N6LSSPB@/YV-3/3/3.@]<@;B`/_%;QF1Z?\?G^U*;)Y1V<,OX41_E6C +MF7GOF%ZS_/Y[]6CB\0#>$]0IG2'("$^C.U+C.L/,RIUEYB_8ZBD'JHSST;G` +M_:E+H`.K*WSL^@/JFVUAE +M?'Y@NLFS-;UBGCERFD^B>S/X:3Z18/&>2>]X3J!G^GK/[O30/(9VF$=M#_&S +M&T\S>I^EO.FC=_Q4]O3IZ@AIZ6'I%UE>AQY8>ROJWZ"%:*&[` +M75$13T]%M/ZU=4BWSCF6KG(\7I8:9ZL`?\C'PIGB?&^Z]X#HK2G*I+,BU5\IGHKD$ZSZR@`LT6^7@2V=G,SZJ91M:LWO$^MMN&Q&X[;8]HN]?DPVX?T2?F'U6;J#P4/E=N`]%ZV?]@FBS"XJ!VVZ6IX+LD:$C0CCZ!BS[!S\C7(-KGD[\._S*"/@)MNY!]Q;,Y(JE% +M^T>LM[$]YDC>B("I>H\[DE>GWIV`--H;V(BT])"INE&35NN.^&K-D2#LLXWL +M,OD%C<)W6%NHRD7P)>37`[[6"A_WI-L"NUVQ[YH,85/`MXE)OM>*,\&I;=,] +M9>#E:E<%Y$CR"X;8B`#W>QED6#Y037LJ.Q`WLXQ'Z+[#",D09:.?+2RK(C]Y +M[7BB7:GG--*'J&YL7C0AW01^:=]C!7@R:W@R1WS;W.#)#)Y0-T0?[>M$WS=* +MW,$C^LGN5-BV.J)9 +MI=6HON09282OTH)\A:"G,P$]_QQ:WC]XB_(3+=`E:WH('2SD!!IJC,H=86HR +M[3$;IW%JCU72=T%;XFB/G.S(@+NR5&UW(]8[DJB?V@,>C7"DF^KHC*RI:$>& +MZ:AS!]]PXS60CV.$B=WI3[HQ=419R$+]-NBJ.,ZJKJ1^1=X/4-GC8T].%OY7 +M2I%#^F25L'\_+H_Y:.NL/O:WD+0)5SS'-R^9''&G<\?:='Z-QACNPCNDYO&+/O1S/&Q=EJBZ#17359GO%25:;21;3@O1IR:4I\IM>8NV>ZR!]Q +MC::Q.+\"=L\]7)X=&!;W9*;#-_?#-IPBWP/U?JJC/BC/K5E-Y]94?4KTWQRH +M9C>OI#.$J_8VE],^.I==X-N59X;.C`?^RY),G[(DN^6Q`;*%FTPK%RT6./+UI5OG3%$I%Z]X*B +MY73X3D[F!`=)I^"AZ5._5S`U][LS9\LC>+0RLO&:)?ET+]`QYMXB[VQQ-<3O +M>78=0&B#U+-!B1-ABT+O-M#;HM+K0[QJ%V#[1IQ@KE/I)@?ZQIVST):JR9;( +M>WS63X$OCCZE&K*H$O?)RKLVJX6=('BZV^<8JW:B_^Y-7^^@L]IJZ+Q,]G\@M!F&D=;"$:.@ZKS->.@5+R71GT\V&QY]T]UM7I>&NS&OFT1 +M^`FK^?[:E8SN*KJ"[@Y#W?ZOO'-B2:&V_-PP'X[RFI2R6M6RI+ZYQL..F4GG +MD`_^3W6OJGH=57\)&,N(GJ +M^$65-V0+_\4EYK>(+SG^7)^OY:U6^E+[:^'_8+Q\!?CJC=0L*:1[Z:CO]JRF +M,P+=6\6];X`9+%\KW>F8A/[-VG.H*`">K*&61JKU/&M>V/4'S-#]J#1 +MR!:,6&]6SJ9R?]HREHG^^!ESI.09-P*>Y'/$^FX>E#RZ?2TH$_U,,-%\CAB3 +M"%WCK'F%7T3TDN>$[.=W=6M7'>^!'^LE7 +M(?C&DT+?NL@7IKD2HL%;,5WQ6S8$55HVXMU(-G48UPVXV;"!#.PO")C&70;(C'L*K>0_\ +M`C/%YZ[APXE.Z$,6R:WV!.F+A^ZM3:+ZI+L]&T]0.]RX3^U?%5L41!UU*VVJ +M2_@::SR,=(KT'OG2Y3ACHV^H>\WZ4^KB[JB:NJ`,G=C2Q8L7K5SE6%`N#DUS +M/EI:/(5^7/]MQXI%3Q0L+88!G^!`K/RIL^43R'>GN1/'&+*SFZZC\0']/L9^ +MF(DG^J`?CI.V._G4@/O):M5VX_<6'TO-$GHP;!+:U),-ZAV1O*;`JK9K^B8( +M_&5%-A79/2OH+JI-Z_BF`BM/G7,`LD^COKG\+>Z7\ +M=.]4#WNR!;Z5S<Z(5OP/U#VY%*>:=Y+:V%(![]OD_V +M"47CJ?_`>S5\C/'*_%L2\)<1/T@GWNE<19/BLTZ(8.P!FY-'=H>G%-GXZ<^; +M4&=6Z(9_Q'H?W2.403HQ(G`UATV\N"!D8^K]?Z07LMW\,#K_%:'\IUD:VG9U +M#_NA6.N@-D@R.0O^(8-#*..`8P6[&'+^E&1`?L^+ +M,[I#MY.\H:V+NA +M[;#U=%]03=][%=L9?/\4_'[)\3TJZT=U[QW](WO/\T?FF"/>F]KRWF5M6>\R +M5X^P,6:T>Y11`UQ-#+*T1?KMJ>#?0N/>C@#ZD16_(QK'[3G)K)X5;XK?8FP' +M?KW%7336#'E[NQCUJ;!CD5JB,2SIAKPL:GZT)27_CWHH_VZ2!VAJGRUETM8% +MF7Y/D4^>5CX_RB+YT-U1D=6?-X&V+,@@HI9!]:^,":RPI6F>%2=()]>GS[)8 +M(Z>_.%![BEFHWH3-$.E"YC529Y\4]AUU5(-R':B?)GI*_!>YYF:7M&,AOU^<^)>[K9I99WG7J5'3;"9C)/I9V2)F#\*MZCC9E\Y->BSD3'\9RZ9SNJX[= +MV;JI7-5GE1]JAX;K=*MY&WQ4M@TV,`WCX+2>=+H#-JTCT(:Z#K,])X0/:YH9 +M\*?#YG:2C#7QYEJE+T+\V-PU1(?LGRB-[@C(7>.GNV1"T3S]>4E$(^`SHK#] +M>1:Z3YS*U<0EYU8@+]6I$C?@WIPS]!V2.PYYJN9`7WYLH_8:MA8U.);1'8Z; +M-Y$,PFA#X92BAEMZF:D*>BGO7MW\HA=MS!7A&`=M;B"]2.^C^="B)C'_F/K4 +M`6^PE[55?\C:0M[JPR?]%CD/:2&\OZ^7"MZ2H?"2S]D& +M&RUY>K0I#-SAU$=]L*\NUQDV[#C;\AK*:>H(=8HYJCT1]#-?VIDRY^KZ@OWX +M68Q1;?#=:/SN(AM&?E]5F`WSEI.=W7$(.%Y\*TCS>FB3W&[^_6D?(Q\'[5CF_.4-?&`M*5;?N]/65[-Y5JJ*;(Z?*#VG\PBY;)E=T?? +M@(?7+*^F-3&EC'24\0C?M!Q]XY;#LH[$;[(!IM`YVE/PXU##61J?_G@?=U_M +MZ;H$$ODS&"59[UUDW,Y]#WW76 +MC/'8KKRT;6-8)M+&G&!;KPN960:"S7]%20KP9M%XO?TD^>M;GQ3^YG-L?`3X +M:)Z@=@P;3W,$B',6A&U,.T^P80R;$%_NUC2Y+E6;1<\G_.RJLCOH;MEM=+<3 +MQWC7+->XMOX]O5[JWXCUU8STCN:LE+D(C/W3.4]=,H&?61CG2$OO2XRGG$L^PDVMJVASTK][/%8>)-QKR.9OA7RD[:^=X`UVL7:T\YEE/$QX`/.JX'>7W5$0KA8R)'JC +M\T,*']Z3`E^+%QRH/*FREOI5:_*LW,NH?@@>L'YO'V`_CZ\7@@'L..#]%LU_ +M4IJL8X]E#$9I/ +M'`-YG[,SJ7^\#GC'*3Y==BW4OP3X?Q@1AM +M%S8?55?\H.GVPW=3Z>(NUN09#N +M[R;>ZYK('R:>!]Q/H?X>WR_[8HD[T;IXE#?P)'@#CX(WY>[)X^PI<6[]A?'S +M5+:4[0Z/9PSA>.8202/J!/;A\A/LJ4X^[(=-%XBS0<'IVT%W6"IWUAYG3Y^! +M3?>)L2#*@WYH[-#3U517-^]G65(/GWY\C[QO%&/%I\?#]O;*<8BZ9F@2:X9: +M&WMA-#Z=)=<4:@OQ.X/&I$3O$R'5_CV=H=C^^7\$_BGGMC9/8*+^^[D]]=H&TL=GLK4RR@U% +M7-+^/#V1BW;^],3S^Q3/[%;]$2EC&W1S>XN0<\W<5CK+H:J*?WJ<;9\7VKQ\ +M5M@ZMQ<^R%7HRYD8IY1C3!W`&"3,+N9_FJ]O[$MD%FH,B?.!M +MY`FVX_WX^G.(-B+;UXYZFK>XL')W%(OQK9O77&"^!I4._,Y+1/N%RV%'(!&N +MK3O%>2"N;3OA+Z(/:K;3/3RPR7L#;-5NZ-3G[&)O:`9K;LAD&)>F\7[GR&>@ +M.['Z3^(=`>19Y;35[E1]PYV_KX0>8MQ.OP]/<_#0=G.DI,$=*8'=\*'_*U'/ +M,1%]:@4SAS$6WKJ238!N9J;[I8\@^X&==/>W33G;Q*:<;3(1^FN1Z3^Y,@P? +M6>!=R<8WG(1=0_]:\)GT!6C<,:,OG8?_X62[3\3./O&OL<23T911CVY]F%KJ3K:5)KM(OONI +M"^PS?[(_H0\,G*0'_QK>>LL0_:4E4B-]_L@PC$7\`QXYOUU?[=T8T?CY]0LH +MC>*\J/\L4>_U^\CW5\9/;SBB<;(?)UO>*/>,C1APUWM4FT[]FKR_O?ZHSU2S +M?X@];C;EOGFSG*=_UJ+:>,)!\]^KUK"QWQE-\^V,[FWOE?W.LQ-BZ.J*,I[Q]?Y.RK6!)MVQG]C>J?)8199#O2I:.\G]/Z=0F/3?3G--S +MH[W4=YW+2YM)8U[ABSYGO0GY$#?R]2^"EMJU;`+==PT:A-WS7[Y\X-H,9K]V +M.W,.N)_+])E^E";F/*C\S3O9+4A[Y:PO6=;3<^UX=VK>ERKK49T^]EQ;XQ>B +M?^ZD>8E(35U-;-WEN7:%EO;F,L;$N52CZ%RJYV##ZGUB70/O0J?(W_LAC4'^ +MYJ%SJFCNHJ""94D?MJX)_5%UC/_G_HOFM`QM]W/2!^2;:EOQV\(WU1?B:44[ +MJ,.5KQ;>E6N!(=\%H*+`(?T +M"0`/'"I\M`[H3#,:+P%N*^A1][T-N!OV'BF1<]*4-X"\J`.T\X;#ZMWU*E\; +MP`^E[1%^:T.WCV76*+H3H'RU)ZE^4=9*\(ER\&Z3>KKKMW*LTS!!SL=)VJCO +MB:,O1:,C8PC'\@JI([6="J\9DM>Z:L(I^6VH3]26J\;!SVH`%W+<-=D[-L"\ +M#1$6V5SGR:KBGN-L]_L=P=X+]!MVR_[_;#7S5GZ#4?_V!6LT>?WB?BW\WB/V +MO-.:&?6A&..6J&/WPI?C.@-W>LAFB,U +MM?N(!Q?M8PH->*A-.E;3G-V>'=!W'ME4N^_">-@CYB1<(?Z%/Z5V'_ENM57) +MUJD-W-.?6N<9<.\Y=*1"ZL0%XCVD\KY'X1UQ8J[CEGHQST#O_L3]95T3]9?H +ME\Y(631FJ?W2A='1F/45RCBGE+'W7RQC[U!ER#6\ILP+'[LUAH2NU-3MDS;[ +MIR^$W,QRKF9'J[@#ULRJO=TA%K(^6M<6Q+A_];NLAS6)?J`M(,9-P7.I3]G. +MNAMI#YGPYW:98WK6$7B7>?-;F3?4BG:W(PB[U!J!37+U\+"01QE\PH74?^PX +M=+P48YX^9<+-9=`Y>1UF0UJ3&TMI->%6>([+MIC]A1_#X-WP:^XJ[5\!-/ +M,^F,Q_*NY(#5EK6\_5U!="?O4^UM0$&8XU +MJE]U77R[1JY'QLN^!;AZ]7J1O6!%\:.+%W_;<=^*HD5\"5\OY%.('[R9Z2KS/BWU9M(]0@9FL +MO/N]E@B-5Y/EW://'Z%XN0_KIR'RYP?W2'D^W^9C^^0ZO-LEYL-HKG][%?6K14H_ +M]\)HL5:[^G.:NZEN/$MU6I2A^)+T;HV<_ERLHY),R8_RK/R0-9YBEAZVUT9S +MRK']\7L7$7Z^>&(G/YBDIH/)?`1KAHRK;/D<>BS* +ME.^SN+6^,'PZ?`"_9Y/,\,P3;6W]V\>00_)O1PV0/AW>W>C +M-[-$Z-N)DV+?00_M'Q#?3YR4>PYH+X'<^]"GK%/M#6CGCZ0=?.&_Y%[!%VSJ +M]Q0^]L(DDFN2/74SXIVQ/5;/^TE^T.,#WC+"]\(T=5S0;ZVK[Z^IJPM8=V8% +M:FK13[U0B/;6,M0=D+1_G_;N1U*+)M+^_>:`T&4SZNJ(LI<_Z0OVPKK86E58 +MF6-YH5?Z%R_TBCF6S75ESZ\-F@^L]9GWB+VW+\#_^=4$=4\0X!H(E^'=R-'Q +MP<]E7[]A3&&$;'GJHTUB/;[L)*W_#9-C%0DSX/[Y9%7?Y'>4/\_Q,5?9A>OO +MSW.&FE^15WU?@]UW*[S'X?;WR^U+\OD3YG7&"_>*,\OLJ_/Y4^3T*O]]1?H_& +M[U>5M0CKU_ZB1,KZ%_N@;W:I?]$X^+_UT]0Y1\074CF(/XJZ+M7$ +MSU+@^]!W]VCBI\GX?1C_OA#0Q$]2XL?ZV#-EFO@)2OQ$=8U/B7*MJ6\7WN95T=NV^&A][*B,&\_.0DI?62"9KXOU*/,8_O1,U +M\3V)=&A\&BLTBC?5&\=3/[<=8W.QAT?L9?OEQ?1]7_IN?LQ1Q?DQ]F)`V>]B +MH3T.M,^TA[WX$<6)OH7VGJZEO:'(&=I73RX6G>@N!IO5L+ZV._3),V +M[*+<`?>+?3$;]N(T)7[F@/N7%DU\9F*?<4XKM\X)GCO-`Z'3/+CM8]"]F)%= +MN"I<,]=*^QGIC.+0:NYWK6$6Q%\T$V.(F64#?.M"VIMRBG64A9FKC[Z+3.?T +MK0)\L+2.L@#;LYC2/V(S2ORLO23,]@#W[H7J?HE?QKY_`PU4GBM"\^"__(!H +MI;N!,78OX6]2GUI+=Z$[P+4_HWW" +MOW+`QI2@_RXQRK\.8_<0V>TO/&9ID_:G1W[0#)A?;1U13M\OU96A#S3_9D70_/H*CSDD;/G^-#'>)+^5?-6N,.W= +M3./6H@SXE-;P$R4CS_677+0;X]?P*F?&,Z?9)/7<@`].YUQ7+1)CPN3VDA#S5GS(JE92'>U?0'T8[34"O.T$ +MV_]8>I"9$4R>9>0_[G^2]F&#[U;::^U9>3?%_=3UF5AOM,H]W/LKZ1VZ]3>> +MNGC_RV>#R7+GQ(/7+W>\CL]\GN-EU]M+R>_ +M-C\;/I!9[)O>),IK)1^IH^).#[?.[Q(Z54Y[(3^#O>B1Y6V:WRKY?_EBXH%; +M"^ST?8RW0:Q_^F@_3N1T'?.L%/2E\)0"^FZ1?K_(Y%X%E%7@E/,8!9/DG$]! +M]H8QS-:?4I`GY?+R@XYES$ST2=F^W.;Z.]ZAZS1/)CR$W@O +M"&>A+E_*IC/)@>,IX##AN4;RK\))7)"+D_`I9:`-OGR=$E\HX47\E9KX/,(' +M_+W]JYT]QTC+TR0_&K[E;+$3B!#WDW +MJN6574,^PX'+Z!N@6J6_D#[7*V^I\Z@TYZS@30/>ZPG6QP[8Z9LAY(=/\\H+ +M/%7NCY:Z]DJGR`NZ"LZ(-?=J(_[C\QRP:+]#,H(WW$\;W?M^("^ZE[IF2;&T +MC0?V1T[S??PT/TA[,OEJ?@CC.>8ZQ<;0MW%59?R$&+-;B_+1]@NK3K'4*OK. +M"[:#QGP\!?&;B@H)!^VM>T +M[QXTH)TFG6"OCA(R]FD5K%`C0X5=O +M572RQ,=>A3[`@.(W/>D=.F@#S`+`Y;NW:%\M^X8<+^Z75W3 +M0+WZB';P5LQ3E]0-D&PQAO6&>ND[GV+`>M`NNK3?&""/1Y2_*=^PSZ+U#_)K +M:+_9YS9:&VY.32)[DNP-HQDEEWROH9` +M`]K"*CL/#KA_?<#'?I(M]INF\!']-3Y''QW[M1[YTT)F&WS5$*_&) +M?@O^TZ^W-.\U_OZ86^=6A-QBO:,$^O8YY'FI-\B8%SC;]YQE[?9^UH'?M!?* +ML^Q_68^#&?:_$?A>\$^LA`MMI;?J,6;WHL?K@$?LA?=+.-HCO:R]@GS3^I*. +M/1^R#OO_B'*H'_6L[!\"]UP[<#L5W'W`G9$8=^V!"\-](737-ET(;IJ[\-KI +MFP$?JQK-+L;XWT][R>5^G-=&D_\HYJHW%=G$7O,O&(OM,?^,T7K4@/LU2\R_ +MC,[1V7SL-:?<>Z'.G1S\B'`EH*-![M]Y;8$Z3NU/*6I0<#GTZT3J3>V+*LK5 +M>]W+'=>5I;*'%A4M6KIZ4;%C]N,K5]STZ.+%CAF+5JU:L&11*IN]H&+?_&/"U'S,+^NI+3K"_7DEG(Z%M=J'=/'$,,*Y/:7WK +MOTOCU[<.(>^?_3(0GC^'9*#?VO!7FPSZ^'\WJ.7_RSCZSA.^0OE'<_[?\O]O +MX0@,%4B'X&]T;SO-K-"?@UO.0B=/B>\K>ER/L[$8/YBISRVOX#U5IZD//_H6 +M].\0?)YN^`9I6R/,U@%=F=''0S0_1GLVO7AO6QUD[_G_2>O#G@YXB>W=0M=: +MT@I9!OJ)P]S]^&QN?GP:XHZ^$LE*?F5M9S+9[V/LT"E%9V?(;SJ."G\,=!Z% +M+XL^X^BK:./HNX^N5\;8]'N*XC<=HGN.FE%3S:6,08^30>=1ZLM>69N53&M3 +M'1C/HX_:`KHV[AG%J]&.]H&?O>!]_X#[M\7H7\2>9+05#VC;AWYKRXQY[.JM +M@-\&^+82#WWSU21I_..3X.4`W<,D_>SW/U?H:),^S?M'J?\B&JB/QKL8S^P> +MQ6M4&E%FEX\=%/W>5L1+O.^OC[A?*R790";F8^S]/)JK0#F'Y3CK=Q?3=[*@ +M^3#*.@S\A[:BWC;(N75^C+V>);]-_=VD^+;\>A8"=.+U/`18[=?+$&#A7X?? +M^#IZQ=1U^Z.L^!(P:7@>EKZ,7?`,HWK`BP#*^X428C#`+`7C>@)_Z +M!GR[-Y#_C58$Y'T#>&#Y/0X$Y/-,1,A&@/7QH'S/1H0FA,,(70@H +MDZ;JCR#/$>0YDHDP"0&T'T&^([,12A!`]Q'0?01T'T%!1Z!A1U#FFZ#Q3>1[ +M$WG>1)XW0>.;X/=-:.&;R/ +M9N2A70ZT6Z49/#8C7S/R-8//YJ,(D$U+&@)H;`%=+8!K`4TMP-L"&;:`GQ:" +M`5TT+'@+M+T%+^XMT/86X-\"[K>V(`#^+>"C7?]O0UYO`]_;H/MMT/`V8-X& +MWK=1_MLH_VV4_S;*?QOP;P/W'U#^'P#_!^#[`^#^@/+_`'Q_0/E_0/E_(!C@ +M1;6P5I3?"ORMD$\K\K2"SU:4TPJYM`)_*SS-5LBS%?A;,19N11FMD$TKZJ05 +M>%J!YQW@>`?YWP$/[R#O.\C[#NAZ9Q\""GD']?`.X-Z%GKP+7M]%.>\"]EV4 +M]2[J[UWP]"YX>A=YWD6]OXNRW@6=[R+/>Y#_>Q,0`/<>].H]M)#W0-=[@'T/ +M?+T'^MY#.>^AOM]#GO=`4QOH:4.=M2%/&VAIJY0><]M!!.A4&^1`7T"U@8\V +M\-&&/'347#ODT`X^VD%?._*VDY6"_-JAC^W(W[X?`3C:458[^&H'GG;@:0>. +M#N2GG2(=R-N!O!THMP,T=H"V#I39T46S9`C0*2_*(<_'B[*\H-,+67@A-R_R +M>,$;[3CS0@9>Y"%OLQ/UV0FX3OBYG6BKG:"K$["=U%;1YCI!8R?*ZD2^3I37 +MB;R=H*T3=-&VQO>1_WV4]3[:V?N`?Q_TO8_R7I_TS`E&>^+H7C14ZE_%GD6Z +MM\X'N\#_(>_14WZ;8K_E_7E*O+PO?2P/$KR(6R[B_$J<:7"<,PI'S_Y_R+OR +MU/S*NU_-2^]X%BM/O--YD:_G*,\\Y9E%O+CZHK1;Q)U^?0*O12E/WN<7'V<: +M'*?<"Q\/)_DL5>Z47%YBB?Q#TJ_**4&:*7&:DVG+$'QK:"9Z-?%^3;Q&+F], +M%NEQ>$@>;V0H3Z?RM"KR4>LS62F+Y)RLDX\VSC0X+BH?+9PJ'[_"9[*!?(S2 +M3(G3A'RB96CD(^(,Y*/&:^3CR5?DH\%#\O!,5)[9RM,9+Q^G)5:64Z\_VCC3 +MX#BM?)QZ_5'X=!KICU&:*7&:*A^G7G]$G+%\G#K].5(9DX]3HS]'9BO/$N69 +MKH-\;SZD/&O4]JO`)^MH\2ME)1O(7YNFE[\F +M;9#\M?GT\O=KY):<0/Y&,'KY&\#$R3_*KX'\XW@VD'\^#XTW&\4ZFXE1]/"4]SL]3XC0VMS63GK&\Q%^K37DZ +ME*>J;U&_1_!MX#\I\8/\I\'Q3BU\4`-OULI5[\NI_"CI@WP]E;?$Z9IRE3)5 +M73+B29<6QY0;5]JO:3DW9<;Y?@;RU*?KY:E+CY.G7VL;C7@RD&>4KWAYOENC]27CY?FN,@YYMTQY +M9L?DZ=3K9YQ_%E^NTT@_E7@C>3J-]#/.5TPL3V[^V3[^]YE&=]S+[)^^SI&6_?HN^F^'?5OD73-?;-.6;)IH_%WO(K70>T1YQ[N#[]>H>30^S:PDDY;"++8*^M9<0?7^/V7J-[X^]L=\R"9- +MJ=\F6;?_4Z>O6U4F)`^2"V!0_[\)*-^U*?(X>B7MNR%9*#+HT:QO"1FH]4[K +M.E0FZK`3Y1ZF.J3O%NB\1UI5&'`?S?"Q;_7I]*N3\A3G4[T=G:BG45U3`AWB +M.Q^EGD<"MC"F>[*.J:[*5M.^+KD&!UT5WU_%UK-^>['<_QY=7_:4S9?PM"X' +M>)M8HY/K>#FT-H4XB\)?-^!;Z"P9.@OOHFZYIV07VF`C\/C8H9:.2L8B/Q3G +M9[82/.`L_`=Y%ST18BEE=_#_/5^#Y%.`C"GPFWM<@_C;E"1H_*,-S%NCJ0AN][;CD8Q;2%RAUU";7^3YX +M@?;L=S(I[)2+]+*:,Y7G-,`'E'52R.$# +M<0\'VDV6W"?QIV[:?P.\MP-GTH#[3\7J7DC:DW_._=J3NX2-^U-E]#N9FB7Y +M::8LEE;(+FLP\SH%C\>L[.&G/7@1X*(S4".;Z"S!';WRF].=LX@7VF>AG(<[ +M"WB/QO;\_$E\>YEDMXU`O&;_\Y]F$WW*/H6)Q]B?,_#\1@_QMY!EJOQM(WFN +MICJ0=K6'?=!%\KHEP%:2CAXC^8ESY3[HIG1%!U!/'_R4XJD=H)V7D=XK95'= +M5$B]E_JMU"7TY@-Q5H$:KY1].^*SM/`*'M3]!Y-(_WRQ=D)U3W@RJ5YT^*$C +M'SA)?]5XA;92Q6YT1>0WG_:@]D +M?_/A[=R='@I9E^2UAR8*.]4PBF\?<'?EJ7=LQ?3[S_[V)G$6:3?!;3C+;`&T +M.VG3NFI\S+I1MUSI?)*"-:GTC6X> +MZ!@->AL47J<=9W\)*[+J1OD-9.,V+)3W3]!^+;+/SU!_9>?!E@;2Q;]8U?X* +M]-7+=OO'>=1W03?SY+TT?_FIU,N_U,C]_^+,9GHO3[*GKR/=#IE?:VH0WX#] +M)4]W;K`9<:6Q,[2E+41<=4PG_S*-<`!7#>+K8_$2ELX<2`^QR^2Y\-&R>R0M +MLHU0_T-MY-R@-O+'?-%&1/OX2T!3YF%U#T]_@3UMP/VAS<<.*NR2 +M/*1O]U3]#N5UO14RDRYVM:">Z[VH<]C5.GGGSX'\'SV+Y--GHJ>AA'?+6J'>PD7X$4FI]5.?;UM+W49FBSDDG2`>H[DD/ +MTM??+W1`G$>&.**)=`"XT/X/*'+^4#DOZL->[;>&P.5H<83%_NOR<>RR%D>` +MSDK^Q!N:SB@/[<4FN4(^6V`["J$WD,U?)\3ZLO\6;8B^U;HV@SE0/QE(SU%] +MNELRZ!N$_QX+>KJI3\9O*^25:%_R1-IO1?M<:/\6[2-#WW3T./OD=EHQWDI[ +MO-W#N:N$^[7GGE=5\%-T]KEC);OT&/O$3ONW7/^D_1E_[8K?G]'M5,)DQCX: +M'PO=3/]B$&W)Q4XC1.82H +M&SOC&VYXR+.6]C!W/T+TT%G9%_8-5'>IL&-?B#UFMA.@Q?5WE@R?]!\N<39V +M%ZNMXD'99KL_DCI;8)=[S3^Z6-@:X0=WP_Z_=DC9HWU4\0\ISQ25+I^0D3C[ +MZ=(!]T=C8_7=+>R[U(GN3/"_1]"SDEU)>UO3P]Q#/C+I(NJA$S1^5+Z.F9%^ +M-6C8HOHFR&-+"K(,\DFFKF-7HXQJ56=JA9YTD]T]*N3Y!>TS_:@T*8`\L7*_ +MB/V^Y+\(+CU`/DXBF7_TJT.3F:?JVY3O6:7O +MZNY_PLZ:[9-A%SXN]IEJLB_\N\"/BV5=RG-OZ)W.J)%[=!V0P<<3:9\NO>/W +M`3I/#GU)B_0[/^Y2ZP[]1PO2Q;=HRMBB1;;GCZ/[?PD'P77@9VGU[>MKELZ:6#^=5=F;>>G*X=5SP\B:Z[V5& +M>9`YEM7]?^Q]#5R45;KXF6%$(#Y&HQJ&#Z>BW;$E9;O695LL*MVE0B'3PK+2 +MLD(SQ8\4%?G*6""^VLQ%Y:NN_2^[:T5[;2_MVC9M>I<*&6KQ7NKJ.A'J:*@C +M#(+,S'O^SW/..\P[PPPQ(Z;KE=_O_0WO>9_W?#Q?YWG..>_SD&/+B-^],,/= +M&$S`G_BF`V,(?1*,ML,W'5AG=C4)R.ZBQM)GQP7`G*#<44"+K@J^KMX"=7R\ +MD+WG]GNHD?N6>>U18ECB7=^^C7/T[=NX,>Z;TM&W#.C;-^.\ZUM7GJ-O77FC +M[-LN[_N6=1WT[0WO^G:DW=&W(^WGT[><:GJD-8I@G.G@_>S,?^<'FZ*P7V0B +M_/]";C7]EMLWG=&V$OS&H!/\O__-Y'S\+?/-Z(KT[5/,)#C73*G@IVY@\9HP +M!F(!M;9F6C%7@YR6/)?Q[MD!.5V1L9T&/9=5UZ,@[YXU@=W3N=@>4]Q>7UC5 +M2#JK<]N]&20,=1;^YF[#V!_C`^X]@S9&9Z,][H>]KE3C>!J63?PP]@A^=ZA7 +MF4AL-AF7F@7U#Y"%85D\5Q+WDSO[_):1X"U0-_[>J\JE+VTC"HQ1@K%&>)P1 +M_X#^@F]5]G:^!Z>A'*==(0Z>/TV^;'#CE]8V, +MTV]/>L9IE\*!4UZ7!YSZN\=IUWVCPVG7LE'B-(3C],@B!TZ/7./`:=<^CM.N +M/0Z<'M&=/TZ/!#MPRNL;&:='[O:,TR/I#ISRNKS#Z9$:P*52Q*G2,TZ/Z$:) +MTS".TZ/O.W!Z=(D#IT[Q^G1,Z/#Z;'PD7#JG0US3$L\?"OG93V9GNI!'&)\;J'@6&:MX!^` +MWWC`_^CKW]5)CGU8*XP+H`4[4H2"FC4TWU\AY(]78$PEM]_)LN]$C=%^$20& +MXW6`[Z_`__L+C%H#:3".%,?"D7?5.'?H^TEP?G4WX/>$)]*V\-RQ9OY-IY%] +M4XAQQQ&&Q\%C<)CG2B;"!9T@)R:C[XS?+H*M>A7<7P/W?N+SX!/DN`W;%9^' +MP/T1^STMSR,\1LOQ%E;&WYD`,.]+WID(]S62Y^%P_[+D^35P_T(OCT%3GVJU +MB+$GCB?X%I/I>(+H7_RXO^!XML._.*X1RQ50+OG^[[C2X8\_6N^/(U^8=;WVWA?0[%ONU +MKL">H^X[\']O9?XOX%(+]U9'KKG'.EB\O9>>[^@OZ`YWQ+F4L5P[OR[#[@!>?S?X/Y/R.`]\MP#'#64+A\=XZGY9A,]VK--P>*2E0<2U +MQYBJ4T@\QJ'"_"PLQE,1YBD(HY^HS(3GNSEY]VO7\F^G4:93LZP482OL,:NR +M89R!K[/GH/,#6/X*K-/G6-8G@T7?N)NO.72S[ZUY+)B311COG,>?/KG,+B.B +M;,1!69Y#YOA[4+;%_BTY?@/.XNAJ8&PWX/=X)\\`#X=5O$/BG[3*21W\8IQV +M+_LKQM)Y.H_+8G<;R&*>.WP;R*GPL9F_3GF,*P*^32-<37#IX,+X!^3)`9($ +M?,OP8<\](12<"L<]B4YRJJQ64!`6OZI`:&3QR.!]N'1P-.6T"6Z*1R[:![--:W7^SS/(T*1?@V@=?(S/%^ZG" +M5_47F.+M:R`X#\-]DGU-F\?/,J5)\FKB_4)[7DT/<3FP[O?P:Q53[F+9C;6X +MM@3W/&XE/45,['L\>SP[:6P;M]\:?PGS-LN[LVQ%F'71,_P[X#,I%,I?.DL4 +MO+TSL_111D+/"F(,GC/)3T3GX7L!E9A'^$L2C+G/L*U:2?R\"BC#=?C>.208 +M;6"Q[F2,X]Q?<";9'N<+VZH$_5_IDH]ZSD/3;\K*B+OIZ;B;5KO$U5/U!BU) +M[GW34,?SP9T!^3>Q^$T"QJWK3Z_B:VYG]F-\8`%L!)X[],P>Y'T:\%Q1W2:, +M92<#?)UY#Y]!'1V@ZY-%W8[Y@7F<,1M1L5@'@IGE#SQ*S(]C?#R,"2`4G%F( +M^R*@VS%6D9D&/9U.`S/B<%\8]V_TE8B[GGBHZUZX'L'\36#K;X%?L"EZDH?V +M\H;RP_4L`=-.Z8BA9[Z/Z[>>-0:R.0!AQ\F2H*\]FQW\V5-IYT_XO]J>#PW^ +M;S#(?[Y+_'^W@<0DNK1E$]<=S7ROJ0?WZ>Z%ZQ&`[[+G3'U)SN(('/5388R6 +M'NM0+E6&([\P7I=Y&@VL;,1S.)@ +ME&U0IJ%MB/%^V)YT)I'#7.K_9":/7XMYS>@Z;;`]+B7N:U#YBXMMEG0-[FNP +MG!O]VM"+5`PD,SB9+%`Y+$I"P[ZXA)B;%]:$%=+,:D +MM!6_WH0YG^%_+3UK&X!?EO-$C'FN<(I/N4*+L?#0=]#B_('Q*;W3YV;":6E. +MLN=OE93!'&[:P\MZQ%AEY@P#*>@>FM."GJM$?\!`SEBI)6.;.UGN":I:V,-R +M,)@;#*2/Q9SF^Z#F!E?Y?^KI%8N?>7+I"YF:.7/O98@_Q8_KUF*EUS0H-OO24F*QUPS.K5J!H2F4SG.4)E;-8 +M+2RN"X_'TC>#ZP3:QGF[[S;$+@WG$6-RNB4"G7[-1GD>3-M9VDSQF1X`OBW%N,S+F5Y.PX_N=Y`ZN!_C,^`>4]YOE.2`_5\^J1M +MO(;':3B[:Z3\8F[G,?_#2EK8U4!E_H3FCP,51_V/Q +MM-`8`&5Q-']+A7PWW%0EKX6RW`JP`>[G^=1`LKVP`> +M[B.JJ/_K&EKX6B/`P[T:[JL`KULK`3X*X.%^NY$6;EL#\'`?44W]:]IH8?4" +M@(=[-=S_KH,6ULT`>`W`P_V[\/ZN;H"'^XAZZO]>.'W34!5FS:50!CXWZ+]M +MDQ:&61<26A:=$(9Y-;8=AC']AY;ZOY\(=6B@[GI:^)]IU/^##*@C%NK>20O_ +MG$?]/ZR"-N%>#?[M5P +MWP+/]=A'N(^$^R_@^=]W`GP,U+\C#N#A7@WW7V=2_X.[`1[N(W?1 +MH.,$^0#SQE!+K)S3^RK:2&$F(M?DQ#9U +M$DL"^K@T>%(\US&#\ZTE&8LQ#SH\^UGN@-W79>7U_)P6^[\4\U>+_V=Q_]>2 +M@.\RNZ./L/[;"C69-'02QO:]"^I^_$/!)`\UD7&:M1]!_8,L7K4=EOIK,@4& +MRYX5?Y1CD%>S/`R#NX?6!DIC5!@#"-I(PCF.QS&PA.^W8GY!8]'4-401-J"4 +MY;S(XDV8=1N^9/OG6#_4::-!&6D89P;C154(4!^TIUO[-8[[P$<]N'YIT4CR +MNK>AKX\V(,8N>M)FPUB+19[7,"P+@#Y=XGXSBR6):QA'B=7*SS]:0!.LSF+C +M*'DZQDI5Q!H<56\+CM%B[I[!7!4Y5QJM'2S*2*)%&8DM"W6D6DZ;]F<,$!H: +MT\9RY5BT1#.;D.PC).SH#"+/::?"_,7H#Q",[Q?4TG62/`;E0GF4;N-\L$/@ +MG8W[@'?ZM41034JW6K3^EHU:Q>!&;8!^@8G8J@XWG2N+.8C^]V?&WV&<[*#M +M&"/X34,FV"@?%:R=LNAX6UE4U;DW +M#'FXYJ8_.$!NR6#YH<-K,&3BW#AS,CU-_8.-@,=#M-!_ +M&2V4F36_P#G3VH(^$/4/!'VOB((+:,=P3:@_V2/"@+X&F$*_:0`;!<_;X=?M +M/FU_@0V>KV9V*A5`1LLBW/I*+(8OME$>$5\I#,4^!KUNFPD\R?)TT-+HA)PS +MQ`^?->(Z.=QK5LFHWCQ`X!T%51UN:CQKDL-8FUJA#-Y]O/*L6&_5I(6Y*PG+ +MZ8'YBY!7P;<(ADNA6>4'LF';AOX&PM'@Z`0!ZJ!E4:"?HS5"6503Z,I,&F%0 +M"MLF)<.8P/^S!=C]O]8&*Q$"'FVF]UC)OC5]J(_\3"$Q*KAT0EE,&EQS,U?A +M.JO0:"O@,==3S]!^MN]>@'%;>>Q)%GL]Z.D8S#F'-BW:L\!#L11MV;-`Q[-$ +M9<_54^,FMKIHHX:+>7E,:,\RGQ]C5;J)IPXVLAQLD#9F@_!<\E:A+)KEY4!; +M@Z_3"0?M]@+*K:TX(P4NP(%@-LB4E7:9ID'&1J&P/UR,K787P@+.\J`M/YLE +M(Q&NZ7#=:=LV*#:"UA9FBG&PEM3!OT&/14%95FE`H^5E;N,=NFS#+C' +M$HYY?W/,U+CI!1*5>Q+N^\"76T^4"(=Y?UGN+O#MLKMHUZ8,@,/8JF!AMW2U +MD4WX#:R-!+2:.UACW5X$N4X$T%Y"\*:#;6PW=)-5,I!HJS>0V&KPW>QY)F>;PZCPG19S\`:$K@1>#JQHLO/Z[)4: +M8MJHE>_3'B&6"`/J/YMW>)/MNA!X&P2]S'&W<"3<+7*'.YA3P@%_JN_!7_BE +M@3]YMR?\V0)@;BXQFH32F#2]]4L6>QELDKD4=*H`0VT'/L=ST)KI$+ +M&`/P&UH>8T#8J5FX%V$@M3"OMYXUDZ.R<1]8V%PR:9)`,PCJIB-IR65J"EG?_1+D!Z#=$/:(DTW+2,"J$2_F=V&=#0*O)_+=!+V`BZ?!!T +MN4@W8SV9G`MW6`=W6`B_*B1;PT`CV +MB-P"=D&E/6\MX-@&\XY53I)"C419O9S$8RQ!9@.=AK:7D[BZY22V]A38&6+^ +MVR?ZE"P_+LS[R^`9BWLLV.,>]VK1AQV:2ZRGM7)[6[0(\RIB/L;/D!8O"R7/ +MI]N*GI]+GU<%52\E\>5+21SP6&S="6AK'>9^@W&*.7[9O'7"_;QE'YOU.RV+ +M50NZ^6H*]N#^;G;NM%]O/<3.FGG'E^,VLSX'&)OG`8\@CFA(5!L%6P]HD%0# +M>*(K`#^G2!RT'PX^=&SM;@+ZF-S`'([G%\&.OU&P@/R4B/*3FW&G77:.ROQMHY.=\5]= +M9K(3CK*#9`=U863G<"%/]R<$WS;8,'P +M.6?0XIAS!LMPS@F:YDYN+%4@-\&BW'S])ZCOJHC6!2/(36FTT:KBM@,%VP%M +M5+>R(W/8#LCO=MFIYK*C`AF*1[NA?(.3_(0/DY]`+C\[!DGLC@(7FP'XN;;' +M0:=!D`&T$2[^W!,LN[3E)YA$K1\L\#SW#%(^]Z`,'96%K!^= +M#(4^?IG*T`\X!X7-O+1E*&S&I3T'A7G,J:.)(7F=,N5T?34[QY*GM]Y!V-ZZ +M3/F,7I5`A)*,)LQ-@WOVKTGVT-G>EDSYLGU/OT[,7S/2GCXMRD@6W^-S=E%& +MBOT>97X5"KTJ!3MO)9OX.*Z9AV62@'(8FP@;A[`& +MV41VG@KNDW`=%O.OXOH6';U0FP'W^'P$P<8_T&?-3E^%)[?Q[\- +M\O]W>#]YZ-L@_A[PTM4+@&;\O)_`+.X]@?]_- +M,_= +MN@&'?^XO#"^UXQ#[A.?SRN`YP`9P'KJZPSYF3_@L>X=$E4TA";BF1/TB61[5 +MV=V$^6>I+Q^F>I.-X-H0KGO;7KIY8NIZP!?,N[D#1*G/,6%G"&VU:#G +M0??#G*G(/4,B$%]"2%354=DUB^:M(7G>Z=%KXAC>89X'/2]GY^=-8">4155Y +M68_']4\)26K--)'9V`?323+K)&'TUVN[B1 +M)GW>.8(VAK57*Q]._^L6S!S_O1$"96<%0B.4.`?P^4H]'VUTC&]BOV=SRB`)<+D/=KE7NMR'N]RK +M7.ZC[/'> +MT30RV:/=6QJQE]<;X$N]'OU?6JH6^ZOTI=Z!$>H5^ZOQH=ZH-,_U1HK]3?*E +MWF%G!27UBOW-\Z'>Z&'GZZ3\X!LO1,\=B1=\XX/H$<:OGN$;#T1;1^(!W^@? +M,R+]?:-]S,Z1:.\;W6,\\C_+CUX:@>L_24+)<2*$1LA?$I+(O:##PJK(A"3F +M'T\JW51-Y.#ORM_>T"'76Q-PSK'M7#L@NQ'*,0.)&>Q.5D_0<5*>DT3$/(%F +MC+&2>@8S.PR@WS\!WK,"_T(=`^1=P21+6L7@3"P';7F$G.9JR6;X']KL\S,1 +M>2W8T_V%D\">UF2(L<.Z6;P0Z.]+8-;@F("9DVD_O+]@I/=!ST],55$3__:O!W!S_<>( +M`UH:;3+G:F/X>H+&B'XIZ.B]S$!XG/C=(GAOPW)GX/$5\WB5YWI49 +M@VL$UZ_!,<"\HL,Y!M=F>/Y5HTZ$:^^675_6"S2&YPW%N#-+B^ +MWB!/5'"_4K.+C3W`V"2MSR"[?AG4V60&>P9@MF";[O!M41VNLI;&-`H:@^:6 +M]+MLN49J$_.+RE.-Q`]SD+>"C,XS;.$6(.!%P->;QJL.EQT +M=!F1/]E-Y."'AE"7_?Y-&528;?2C?+WQ))Y-"]UTDLAQOU\_T$&J!_$\>Q*I +MS6'G)I0IB\!GM60$`J\(/^D`')2\VOB.T#;.BG[,1E40W4Y"\4P`V`WQ3Q[( +M8VMTN/8X:-%JH,X;K!NU,?8UR\=/*DD-KN$57KL'X!7H;^!9`;0/!(LVBNV5 +M/:\E4TU$!N-3XKFYVJ4DH*7K!+$6.\X*I!S1D)^D$SF>%]BQ@23LT^XG-"3: +MY#VOW:CQI">LJL-%-J`#W3XI\_>=`RP/K5!OT"".V;<#)O?XQ>\#0LT\%A+B +M5CSC![@UD!H\3R%^+R!LS`@4`)>X!O+VV8%Q-K")2P=)'/TC"<6U7H;/;_+$ +M-4^^]@FXU$!=-P!>AW#ZQ$HE8=\7%%Z;P'!J[@!]YZ$)'E+50=Y +M5]T!\]PD+>"S4GA.I6#?DLDFI^V'-W6K3K#_V;RB,B@Q!QOFR<7Q9BXGDT[( +MM+94ZVE*MQF4E1B[$NFS?0#/G++SF4>S@#8#1&Z+.%PEA,0TVMXP:!B=^IV_ +M0<+UWY`LHI2!5MV=Q";1T[-.:@A^L]2R\!"AA3]2,EKE`ZT6 +M>DLK;3-?$XWFW]W()MOHFW+2&Q)#;"$Q"G-(3,`^K+N>0S9F1N(.-/ +MR"8;>=T_?AW+V1JO7*@77-9XI7L`[^:8Q/7=R2Q&WMO;.^3()X[UW:_QV1>Z +MM9V2]=W)S-Z%]JV]!40.]TV\_,=;I.6._1->SF(V%-X<8+<+O]]VF'J1;(=X +M#[9#_&5F._QDP95YSIU.OCG*=YT4XZS\4-^#[/36UTGN?BCYS_/!>O<)[GXOFWL+(IF[R?Y^+?\SS/ +MQ7_@/,_%E_+Y++Z+SW/Q];Q\2K:TW#'/\7(^S\6;1C_/3;I(\YS&PSRGN]MV=QG7S[-%X^ +M32DM=^CD:4J'3KY]\^AU9GIY'_M\$U_W!'LT!]W!'O6'S_[ +MX(K^D.J/VZM\UQ\_V^RL/^[XX/SUQ\\ZG/7''3MYW0D/>J\_[GC9L_ZXH\Q9 +M?]RQD.N).YJX_KAC#2]/2).6._0'+^?ZXXZ](^D/Y-O]*A>^?=[.MXGM+4-\ +MF\CR'0C`MZ_T$$4IQD,Q`=\^@7R;F#\K^S0-,1#,;>70/]U<_]@B#$K4097P +MGET/O9W#]1#R-/(V\K4[7M[4#?IGF1^UK=-.1+[F>N@(0?Y&WJX^RWEZAX2G +M4>>\F]/!=0[P,/T3Z!S@:2DOX[="R,?F==H8ZSKM#C\'80;B? +M1->).F<%USGX?3'R<>URU#D]3CKG1@.1H]Y!7G[M+-,Q]/UW)^G9['^7AZ(B__ +M>8"TW,''O)SS\?32T<^#5_;O+NP\>*?AROZ=.]]D>KWOODG2M"OKFNYP>I?6 +M=YS>773%AW:'TZ1LWW%ZST'?;."96H<-/%/+;>!)R<-MX!D'9J,-7+0D@<\! +M]Q8#KOWT`P,$RS%.RQ7;&&WCN]M\MXUGU#O;%#,/G+]M/,/L;%/,U(GTF^>] +M33'S#<^V\\!E_KO_ROSG=OY+ +M/H_Y[P&7^2]E#.:_!USFOQ1Q_KO?A_DO983Y+\5E_DL1Y[\4>_^^=* +MRQWSW_V2^2_%B_GORG[?A9W_9E^@^>^?75>GG,?\E^IQ_AM95S_8[M#5#[9[ +MUM4/YCOKZK3;KNAJ=[IZ=K;ONOI!E_7/.?GGKZL?=%G_G".N?Z;)O-?5Z>HX7ZY]7]@$OK*Y^R./ZY\AZ +MY>$XAUYY.,ZS7IGWE;->F5MV1:^XTRMSZGW7*_-V.NN5A[\Z?[TR;\!9KSR\ +M5Z3??._URL-O>=8K#[_MK%<>SN/ZX^$.KE<>WL++YZ9+RQUZA9=SO?)PE^_[ +M@_.#'?N#\WG>'N#GX?N#Z1_@_J"#GQ]9Q/8*D:=-`P2?N=\WG)1\9=_0UWW# +M=)?][_D?G/^^8;K+_O=\\_=\Q_[WL'W#^2[[W_/%_>_YXO[W?''_ +M^Q&MM-S!W[R<\_?\$?>_<=ZT!7,I87.FI=X0 +M^WWS9DXF\*%I^+R)C=G7C?,`S[.F8]YS)7V?WO/<'Z'[_[-@HPK:Z;N*WZX.YPN:/0=IT\J?;.7%V4[[.5%;)U+J)J4C'/N;+`9P`Y5E*XB +M"IBCR2M@`V.,-7W7/I*9@S;'HLFI65*;X\DOF+V!<9_0%@';`G-(V,IB&JU@ +M)Z.-,61/+W>VIZW7&V(M(3&[>[<=KO+&GJX1;8SJ?QI[^@F%[_;T0K.SO?'4 +M9+N]T5OFJ[VQ*-G9WG@J7*1EL??VQJ(^S_;T(INSO;&HC=L53VEMF)-&MJB+ +MES]9)"UWV!N\',IDGQ@Q+L%3":.P.9I\M#FTE[[-H;I$;(ZGOV__:[>++H_] +MOZ'+GTKQ79&QA#[?3AM!?Q)<8JX1+QBKL=+'Y]/M_F.SV?C +MKM@;[G#Z3*SO.'TN;R1[P_-ZQM+@SX?LC:7!KO8&VA>S!L#N6$L4N,91LASL +M#[`]](8/21G8'R68GW$9V"#6MT0;9,D'SC9(QCQW-@C8%QHKV"%H@]C`SK"\ +M88BM/"6Q1YYUMD<&RV*:!J\W:"T1WMDC]C4/C&_]SV&//+O&=WMDB'EDZPOK'4I?UCZ7B^L=2.% +M"[3^\<\^=RY;Z#M.EWM<_QC95U\I.=^T4NN=KYYYP'F>7+'IBJ_^?7/C"XV^ +MSXV9+N>?5AXX?U\]T^7\TTKQ_-.*V[R?&U>.5[=Q77RF> +M?UHQ35KNF!MYN<-77SGB_I?S_L"D2VA_0'.9[0^LOG+^R:TN7SG@NRY?X^/Y +MI[5FARY?:_9.EZ]]PUF7OWC?%5W^?;I\]7FBLR]/F+1%KNT.6\W*'+US5>.2-U +MJ9R16N]C_*N-DCB/&],\GY':8',^([7^O2MGI-SIEG5[?=LE<;&R +MOS?^HV,_QVL;\0+NYXR5C7BI[.=L2K^R?N)J'V:?1]S''(_Y#T9>)\^/*XMI\G9]?+MH-V[_IUD?WW0>YU_S7,Z_YG]U_NOC>2[G7_/%\Z^Y +MUWNOV_,=YU^'K8_GNYQ_S1?/O^:+YU_SQ?.ON1IIN4.W\W*NV_.]\/_#+B'_ +M7WF9^?\O>?3_1[877ZYRV(LO5PWS54$'E?7P?;HA7]5P2-0[+\]TUCLO]7G2 +M.Y[VXM`_!1LRUEL?]9]O+R[_/.*KOJQTUC6%,\_?1WUYL;.N*8P7:?BV][JF +M<()G.[(PPEG7O-S-=4KA#.ZC%A)>_M(N:;E#U_!RAX]:.'TRWO(5VR7]T7UCT^F>:F$[1G&M4F!=:)>4GA61P^LT$?\0$@ +MS^-RS=2XP@I]SK*2CV(Z%.]NAS(;4;6:;:P.L..@/[^:50OP[OJ`N>S&3:8& +M[WCG5_6>\!B6'T-T-P/N-$3N[KEW[10%>&H'<]*^=BTUYVXC\@IA?`#F+ZIC +M>7>+UAAD$2S7*N8=PGQ%,$\!WQ25?IPU/%_0JL4O+-*L6?+",RM>7*.Y:7$0 +M+HA(VE!B.]2_J.JUB411-Y$0;!/JZC#(YV2S?*YP+P>8W'/0CYSQ`768'\F_ +M**`._H?VH3_%"GM_[/3.!7I/`=W!\Q(53P[+&O]+NC&=P+MFH=AHA'<2['R# +M>;[[<9ZE&7W,SR-0-F`^U9)I0'J$42B.2:6G$-,PC7;Z6:#"?=1GH +M45-91'R(+$\6DD'DH"OC0;;B1;EHQC[^#:1\]FI"CB42/_#Y-=[1O82M2]'" +MXJ8Z>!?UMI?O)WGD3\>X&]V/^Y4X&'-6>Z!W::Q0K@9ZJX'>:C?T5HOTOJIS[,?]BHF/^Y4&W\9=&C^* +M<7N@=UD4C!OHK09ZJ]W06RW2>\*78S_N4B,?=VF];^,NTW[_N",]T+L\7"B/ +M!'I'`KTCW=`[4J3W[25C/^XR`Q]W695OXR[7C&+<'NA=$0SC!GI'`KTCW=`[ +M4J3W8_>,_;C+^?FGPO)*W\9=X?'\BT2O)8#N&D9S?58"RRU_5%:934,B$J[* +MSY.!3Y9XU4$B!Q\G(\Q``F&L&;0\>B'^"E"/%72_+3ABVF"1L?F59XDFUT24 +MK3D&`CZ7J95\0T(,1(YK*Y]W-Y'EJRB%NI]!OZC\*(D'^S&/POQ1*1`-U!^/ +MN(2ZGPJ;2V1_>6J`(#X1M^!#Q3.\@@7UP&,]1:OE?'P!V[NBDI9%)`DP9WE9K\?]/PF^FP#?PWC-@>]7NP'?32*^]UQU +M<%R/!WPW`;Z;`=^[1X_O5P^XX+ORA\'WJ^+YAX@V9WR_NH#C^U4"^-8!OAN] +MK-=C_DN)'@?^'CZ'.?#]V@(:HA;Y6PW\?56G>WRK@;_5P-]J+_C[M9G.^%;_ +M0/S]FNC_J5WX^]T@+X%OE; +M#?P]X4L/^`;^5@-_J[W@[RWON^#[!^+O+7DBOEWX>TLBQ_=K!L`W\+?:2_[> +MXC'^N63>`OX>/F<[\+TUD89$BOP="?Q]>XE[?$<"?T<"?T=ZP=];;W+&=^0/ +MQ-^O<_NO--*%OU_?Q?']>AHMBP3^CO22O[=ZS'\MP3?P]W!;P8'OW^P"?(O\ +M'0G\_=@]'O`-_!T)_!WI!7__YG47?/]`_/V;A2*^7?C[-U$WN;92J2H[SJFF^V2C;/'[_,I*- +M,ASGV[=<.#ME^Z:+8Z=L3W9OIVQ7<)QO:_#-3MGN>?UK!#ME.,ZK`RZ:9BV.OU,2[MU>J +M1?^GNM(W>Z5F%/[/<'ME.,YK31?.9JG]ZN+8++4[W=LLM0LYSFL5OMDLM59? +M;);A.*]??.'LEOI9%\=NJ5>YMUOJVCG.Z[)\LUOJ,WVQ6Q#7B'.T53C.WPRG +MH1'*,,!YF)7(&9X+2)$4WX@OM%?8GE9Y]"J82Q/I"A6QAD9O$+Y5$4MY=*;P +M9#I)/7Z8ZJW-1*\X2Z9:26CKYE,D=3WTX[26//`B`3H/D-F9)`S/WUC+8IKU +M'2:28R5*_=D![)])GW>.Z#<+A'ZK):V93<36JPJVP!A2NTF@+20F>865TMG= +M]*S0JPW&NO3I)B*$Q"38RJ*3

@GAP3RL@9PO8``JLR<Q_C?NWY`>G5Y".]FD5Z-8X-O?ZMDM/KS6K?Z/5O'O==1[*7'/12 +MB_+UUAH:JA;EZZI.S_122^1+_0/*E]I'^5*+\J4>(_EZ*XG3:V>*;_1Z*\\7 +M6TM"+U&^_CT6Z"7*UX0O1Z"71+[4/Z!\J7V4+[4H7^HQDJ__)^Y_O67UC5[_ +M/HK]K^%VFH->D:)\-;33T$A1OFXO\4RO2(E\1?Z`\A7IHWQ%BO(5.4;RU<#7 +MOPO_?9=O]&HP^&+C2>@ERM?O-@.]1/EZ[)X1Z"61K\@?4+XB?92O2%&^(L=( +MOG['OX,H_.T"W^CUN\JQL0]W5:%]2(-CDFVE$=/"\C5$/V`EUN"8!$MI1$)K +MMXW@WF/J^L-4LY:,P[,5G;)=DQ><8^?1XFO!AA8"JC(KGB(*M+'1KM;W-!'$ +MO0=:**L19D,'Z0*\M69V`&TV$!O2'O!K*XU>(SRO(G507\AI(HO)I$*N"6S^ +MS`&2NI(0S0:T]7>QLZ?,!L3S(,]K"=!L#=K[8T.;WXOGP7ZO$^3`9WD]1&_] +MFH1FDT3\YN24[/=WN]C3`2)_Z^S\S6A;'OU7L?PI\#4R\#L6Z7->1Q[9G]U) +M6D';0GM)+5T=X#/\/M@WGMBU],O[@\ +M\V//$.X3SQ-OM[GGB[7DN-OL8\<3;&<7Z]VCL +MW$8=VKF<)]0N>D+M1D]&<)]XUNN>) +M=Y>X^`5CQ!/O9G">>'>:;SS1V#(VMO0?VM&6%GG"14^HW>B)"5]VRO[PS,7E +MB3\\+O)$,ZZ%CCU/_$&,7_R>U3U/O+?)Q?<8(YYX+YOSQ'O)OO'$'T9C_XW" +M7M]M1'N=\T2DBYZ(=*,G;B_IE.U>?W%Y8O=*SA-@D^/YLS'GB=V)G"=V*]WS +MQ'^\ZN+?C!%/_$&Y_@CU;T"42><-$3D6[TQ&/W=,K^6'9Q +M>>*/+XL\T8QKR6//$W\4\WS_,=8]3[S_EHL/-48\\?Y.SA/O9_K&$__I\?S[ +M]^VG#_<]/@CWWO=H>NOB\D53S87U/9HR.%\T);KGB__\\,+X'O^YA_/%?Q;Y +MQA?/KRX?/&G]R^L__&G/,X7?YKKGB\^.'!A_(\/ +MVCE??%#O&U_\>33G_]V>2QCN@WPXPWL?9,^!B\L7>_9?6!]D3Q7GBSW+W//% +MGT]>&!_DS]V<+_[^R,?C+BY?Z&P7 +MUA?1-7.^T%6[YPM=](7Q1711G"\^,OK&%Q^/8O_/_1F4X?[()Z7>^R.?1%]< +MOOCDF@OKC_S5P/GBK[O=\\5?;[LP_LA?IW&^^"OQC2\^J1J;?9!]+79?Q"+Q +M1B+['O\L?4LY@+C"2OPA)T?;,$QS8-`WP<.':9` +MWV3KWU5DA9%2?583:5DX0*9FD=#E!RD%^%4T5TMF+R.!+5U&>-9$;'IM<,M" +M`VGI:B.YBXFR9;"99'=14TO>IR059@U-#O+#/I:#&+\5L_Y=2_`SP'-<0_@][RSSX-YY^]>ZT% +M=O[YDX1_]M[GK=^R8R3^L=KY9V_RY^E-P#][PWWCGWVC^/YA-'LFS<%VG\4B +M\5E$_FD:SC_HL_RMYN+QS]]>/T_^:;:?F1X;_OG;0LX_?U.XYY__*O;6OQD= +M__Q7$>>?_YKK&_\TC^+[Q]'LKWR68O=M+!+?AO./VHW^0=_FTT,7CW\^/7!^ +M_,/]H;'CGT_K.?]\.L,]_S1_ZJT?-#K^:6[F_-.\Q3?^^?S0&_]I='Q +MS^-?UI&8?^,9M^FM<7N*UDDOA+GGT@W^@=]I=:+:/^TGJ?]P_VK +ML>.?5M'^V>_!_ME_G[=^U>CX9[]H_^SWT?YI'YKT/]N4W%X^'OOSJTO+!OA3CX7Z9[)Z'OMA_87RP+UHX#WU1Y1L/ +M_7W!V.T='=CBO1]VX*:+QT,'HB\M/ZS=R'FHO=0]#[5/N#!^6+N2\]#?#_K& +M0P?JQVZ?Z7\ZO/?%_F?)Q>.A_UET:?EB_Q/'>>B_V]SST'_/NS"^V'_/Y3ST +MWSZ>E?R?KK';D_HZRGM_[*NW+QX/??76I>6/?97)>>BK)_O?,Q>/A_[W^*7ED_WO;LY#_YONGH>^/G1A +M?+*O#W(>^KK!-QXZN&SL]KK^L=-[O^P?MUT\'OK'+9>67W9(S,=YJ-H]#QVZ +M_L+X98$E#60X(U2\D-M'A%97D/":!*^;,N +M@.V*#\N?9&!Q@F6&?*"C'RU^M2KS9B([(3/,TF]F\=^;>7QRPRSL>W^A(<5` +M7BS">+;>T<:0(KY?:B"V>'S?@/V',E,@X+.`*/!>**YL&`R)-N+_Y[8=K@?: +MJ-!WT^6\23IEAIL&0R;%:^:$4?@_%OHK!SAQ#8*@I]LQ40K68Y2>J4??.VK3@C$>I(HL4920"O96,OBX;?;^KA69(C +M!C<;LPS@4J!/[0:9LA+[U+N66N&^RT!N;)'B5?+>31Q'WS2;`C-2+&MI6\77 +MA$`?Z.!9VI*=1@?*'R:@KPZ2W$4DZJI\:DJUD;"K#H;1_6D'0?;[2'8;-;9F +M[B'91TCPIB4D/#>-FG):Z(`^ZR!I-<.5N1_@#L"UA^2N)XK9F2;,)1#,"8+I@ +M[,E^F4317_BMTB"[>IF$GAK`J[\=5H299I!-V.V`830<@H'G4,^K4,RL>!CHJE6L5R-<#]48_T`EYKQ'=A[$EU=CQ9 +M39B?8L()V=$/4K-UA,6H*8OR,B;,49;_O-7:`WKC(\IEY.@WL\7Z\)F=5MAO +M']LPCF),Z?K?OXSH>/'Q,[;B.>XQ_Y%$?ELZ\/C +MAN'Z\+N9#GWXW4]]TH.6$;I@^+C)7N]>&)KY@^+#96>C>&$SN']&&@ +ML=))'X;&+.PO/&'U31^>L`[7AR<6..O#[SSF^J&ET1IW^C#5>IIRWHCVTB?[ +MKG0$GF\:N:VH)B_;\AC_V2,?OFF(YWS87>,]'W9G#.?#D_L=?'CR`Y_XL#RJ +M@?/AR9>'\6%I5(-[/CRYB/%A652#=V,X&3?$AR%1#4Y\>(,AOK_PY&;?^/#D +MYN%\V-WES(:ZIT6/-H/N"SXA.U5JD_.<3ZDVVD^+'VVV%4PC_>!W8(XG +M6[\VF`8]'4,M*B+)\13+\A"O)>%PJ6I$O,+_\9C'J7PMB0O)5Q)[OJ:0;H!C +M.9U>-V&^)ES#$`*A/5#9*XV@TDMG:0:&FO5HZY!I_H4Q(8>[H`=4.Y`GQ"F:T8ZK9"W:>A +M[E,P5C'?DVVC5NX=CD]YG/]Q[2EU4PS&1#D6EC4^&=>A>C=F>,G?ISW&/ZYX +MAT31@(JFV2\?IJF@<3`/$,9E:1TX26A@1=,^;;>W;>WVU%9)`6T#'T5#@V/B +M+:K#E8.JPXT@'YJCLKZW/VL'67N4!'3*^MISO\(U"U."\YJ%:0&1G=E)9#V[ +MP"MK)P#G?)E93IT<*SV&^7'*P>=)G2PSZ-;BVL&9&L#?=R#78?#_$J`3R5Q% +MKCXA.Y.!ZTMAYO')MMQT8E5-2K*4QBAMX+<(P!NYQTF4OAN8409\#CSPI"V` +MY0K3/?49RUN7VT="@#^&^'96=]C`IFXJ\!QTAP@\"\5\=GJ3D52#_V1;G3'1 +MU)MQ->:A"S43974/\-MJX+<>$A=F$G-;BGQ&"W]55=?#>9CQ&?+P"N"SY6UYT8CSPH&S=9KODL;0!S7!Q1S@MIS@6(>4)9_3\P% +MZII_#^J58]X]$0\RS_S4N]_!3[WL6S/.3[TMERL_?9[.^,G/$L'X)%-O_0/@ +MJ;?)F5_,'?\7^05Q]'GZ(+'CALE>4463)I?(5E3"O-=/`UP?Z"_L6&$AC(LL1!KI5U*;9]O13EG*_+V==? +M^G8"[8,IJ@\P499$3M_GD0,?K0#X41V5G +MOX!?.?R^`+]^\#L=Z_)N3CG+OG^'\I#F>F-MAT@B4];%_V22]M +M@'Z/\8]LP=%IUJK#F8#/X-8&*_GM":,\=7(,">DB2K1Q9G5_1-'.*3]!8@=/ +MIX=9-J:3TJ4D47A.%?3*4:(8!!MR\+F,L):%1@*RD-EJ-I!:@*7'TD/O6T`' +M/C_X:Y*JM>ER#I$0T"=*?=;;;/UWW'JY1F\]0LJ>!?T$-FCN?-#%UCZR8%$> +MJ9;JXJ5O.G3Q:=#%):"+1@"X- +MK5E.PBTK0">?SKAZ6P[7R=N7@TY>`;;J.9?DB0Z(514R#=T-3%M.!EJZ_8CZ@-/"+E+:0Z#132$0"W"?C\YQG +M2$BJ]IS.0;,C(LT.D5?.`LW0=X#Y)!3TNYUV@%\YXG&(=D]\Z3R/BK3;W]V, +M.3#'#\VGC':'B'TN!9R'[N@!VHGS:;5(NYI5YS>?VE9KY34B[;8_ZBWM!CW& +M__X>VE7Z2+M*">WV`%T:@7:[76C7)J%=$]PW.VC7YR7MQO5M1_(]-. +M[:O<&1VT4X/TNZJSLN7=L(:'VGGH]Q%)4EH +M!W*G!KE3N\B=6B)W:I`[M43N#GE)NPE?7K:TDQ./\8]'IEVDCW(75>6@723( +M723(7:2+W$5*Y"X2Y"Y2(GDF[VTLN7]K)NGVDG:]R9Y+0#N0N$N0NTD7N +M(B5R%PER%RF1NTU>TNZQ>RY?VODU>$T[FDZL:&<^Y:"=936GG9UN-A>ZH3U) +M"R,3D'XVL"^9;9FO"$>:W-^%-/O=D'UI'=&^?-^)9J5K76CVN`?[\BG?:8;K +M-99>3K/MPGG2S+[.;*?9(:#9$][2;)S'[S]&IAG,]C< +M-HQF?'ZSCCB_C99F+O/;946S\4J/?C7XZS;0>;AF&6H$7]I*C\TV@R]M'9]< +ML9S$6M:!+PWT*SU%$DN6$X7PO"H(_&K%8`'XT]^!/]UE(@+XT_HL`_8WEGX' +M_K0!Z+-P._>GO['[T^\[^=.O+!7]:1'_"XZ`/RW%_Z._\^!/Z[[7GT8_>@?Z +MU*,CG&:6X*-8&,``T&^]/#K,'1 +M:8/!$>&EP!^V7E40[84YJE<58%J=$8;RPG`OZCCTOX7"Z.2:ITA";2>99I^# +M4)=9RKC>8[IMM6.N>J6'D-2&<[J<(T`GJ`_7$O590*=-0"?@:R=9^0IHE65S +MZ+?U`6P]^GSF)+8.+A#,U]Y&*WZO*25Z"]?EK2ZRN/^Y^AHI1Z%7(%/#/XUT,HTG%9JD"MU +M(OK,S#<>1JN37M)*](\O2UJ%;#E/6HU"KL`'!CM#*(R9X896(%?4M!J%7(&/"_XRT*K;#:U`KB+WH`_,?-UAM-KD):U$?_>R +MI-5$C^<_D4Y(+SNMK!):68!62)O!4H<=:%LM^DNKI3YN=#+SD8`VS'_%F-?; +MN,]+RZ+3I/8?VGQZZX?D`4:K/S!:#=')$XV^N7#VGQ5HM.-\UR1<:)2]"&C4 +MZ2V-PCW:?U;PD]"_19\)]R!;NXRD9"U1LGU(H_,^9,51\)U6@.]D2<<\IXGH +M,Z'O9/>;6LT#;!]2;S40])?J`![])?2;T%<"7(>@KU1Z@C#_5>HK/=&71YQ\ +MU6=]]Y6J3W$_B>T]7A*^TC6>SP2*N+<"7^NU5L!3,RD>),I9X+L"?QU#_Y7Y +MKMU>^4$QS^>S3`]G\'X$&6#AO"UH3K0:=6=)(&&@NR$1"1; +M05Z$$"XO]R^@`T@'E)G/#[Y)4(\!+4*XC!QA,H+T&=%'>O9+G_>'&8W$_>'M +M=AGQ=7_8OFZWPJ''O-X?EE_G>?_?0:/*,:11(]"HTH5&S2*-F)_DH%&?%S1R +M\8TN*QI%*+Z?1NHQE",URI'1F49J48ZX?^2@T4DO:.3B$UU6-%)[7/^6T&@, +MY8BOJ;K02)0C[A1($=15WWH!\T&>XWYJ>R\WSYBD=ATZ!_=#[X/V.6DI>M-DOL,T.9%+^RYISS1 +MIOG[:;-\#&EC]WDDM-GN-6VN]WC^863:J"N]HPV??SAM#KFAC;@V5Z9N]HXV +M+O//946;&SSF?T0=A71!&E4\16)+.DFB!7!N^4Z%;2N05JU`LU"@UV?M)C*X +M+IV4`5WT68VD=:$!=.&?2/$JHOP\O8?,;J`6?7C_6%7LC+(" +M9<42&J%$VB"^+=^J@DHV$*7M6U5`-LI0U_CD09I.RI\"V`5?$SS?_/E!I(/% +MHCE3F(O[0VS[^[T*S2>YKUNZ'9C_:XH5FC2+-*-S3;+:$9.R,[.IJY +M^+"7%'"SSPE[TUB<>):TL*\:(5BY^%\8%\)Y6 +M\3-&1RO[/#9:6KF;P^*-PVGE\)6=::7>[:#5*.8O;WWD?TI:_=3C^A_@`NH5 +M&BOE0A-<.KB:H6P$,A+7E*V*E@O7:X:X2Z],ZC?<&0D+R +M-:R^D&XY$?L73/W[C`Q>IHAS@O&TP_AK>QS!;X:0D<2P)TG81#M[[J4@3)8PC<:B^(J/1#I=K(Q%`(YT^ +M@Q!QW!.`-@WBV).<\2@SV/$(,`L`=B+`S+#7:RM@\73YN`NL5R-?[#<2Y&6L +M]VJQSF0^9I9G4J2U`I_I1+AP-IX@/Y--ID@9#AL@A;V&P?J?*P+8M.&P2BGL +MM1S6,@-@YPZ'M4Z4P%['86T$8-.'PVJDL"K>WW$[`7;!<-@D*6P$KU?8`[`+ +MA\/F26'5O%Z2!;"+76'K&!^0&T0^B.2P\FD`F^%F;'ZL7@X;Q?LP@&-;YH86 +M4MAH7N]$`\!FNJ&%%#:&PP9T`.P:-[20PD[BL$&;`3;+#7X=L$'!\0"3#3AI +M;A\,260W'[ZTB?F_D\!,0OT5N\(O/ +M=:*LQ7+8\>D`6^H&OU+8FSCL).QSI1O\2F%_Q&&O50#L%C?XE<+^F,->UP"P +M56[Z>X,$5LMA(U(`MMH-+:2PDSGL]VI%W2CIYZV2^GY* +M@[1*L3[=\/HT4MA;:5!\J0B[=SALDA3V7VC0Y+TB;/-PV#PI[#0:]),L$;9E +M&.Y1#@K`%N`ZZ38:=$N<".MD!TCFX=LYW6\,![KS^"?YB@YW-D;FS>2J$_([ +MNL18(SPFM_SGC*^$JL.5J69JE995R&G;5#-1L%BW(3'Q!B@W11QN["^YPV20 +MJ3-93)8"(@.[:*`"+HQ3:RLQ-L)[`WKK*=*:TXUG.@?U6=T$ZKR-KLA(A'8Q +MEN[B3KAG[8+=`_-CBV[5(,(\8PJ)J:Z6ZG">,+@Y3V_M(;J<$Z13\LP4,2F-O;?M<)Z' +MO=7O;!\1>;*5:O7O+AV7=;Z#1CH9:BO.V;(S&"/ZW+NP38.L7:A +MC.,YL46A\;!O#7.LOKI?IU,/DB[Y=+9V_8EJD(CWX_!^Z@8B.R%/-.\%J]-= +M'7HH[\7SFU`7\+Y,7WU,AS'1X7TVM[9V']/ILZ!.7C;_$Y613%F%=4Z?X:G. +M&Y0D'.,DPV7<2EK=3T"6"V;BOMZB^9OL<@3S0R7@Y* +M+"K?2HT[-R2-,Z];)JN`YPA;"V4(#[!&@WQZW%#,:OGTG7J0%(#KYGBZ,P+[ +MBW540-MZ3]8SL`W!/I2">VLA/(]\/\>:*<9XSD>E:?6T)([M=!>,\:OFRNJNI5L`KEFC)C?!>^=@S#OA?X1_2^SK7LZ#J65X#^/5U5Q+2_M+[FS' +MV'0XUC*XAS$V`VQE;^Y<&?A1LCI'G3NQ3]@/C`WV.4BL8$DGMPP0V>>@::JW +MTGJXJN&J^@R>?0I7,Y2WP.]G\/LI7.#;J;#?J[.I<6H5N1'K!WP^TBF?;<:^ +M8ZQ'&G17AE.\/'G200+/X=?$?Z77W>!QW9TN_E\/,,WB_[OATGW_Y5J?I^N> +MN-'#CN9*S1@=W+U=1/Z+1+C6.)?_(H67/:#B]P_$BK]I<`&>4J#^E%)>EE(% +MOP:XNIW;3X7W4_.0GA:,]PZT-,CO2D`:57#>D@F4E4T#WJIV\,_=DYWYY^YP +M%_ZI1#UED-^95\?Y94_OQL5P?[<*ZMFC>8+5P>RWBA/@\V40!N-\)-OW +M1$,Y^HBX7^R'YYV1)_%7;XT'64HF0G\Z`7<_'N0ML%-^#YF23>*Q/M3M,(Z] +M=2B+O"Z6AQ;K_PRL>/RMQ?[`(/WD1X4VL`ZW9+Z0/O-<+[>RF,$?4)M!F&\?.AK%(H@3K6#K!U$:AG'_2E$NN!_NSE==U+ +M/LYVIK58WTZD.9/G#6Q,H+ON?;V6/VL"F"9J229T8SJ/T\^?[]]?O8D:'?V^=YD]SJO8[]W8'EVW@,VOV`:^+]:7C/79<34%^L'Q=4\VE.]$ +M7F1X`SW:FXN\>&\FO+^G'._7L7L%T[%#S^]IPN=Z\+$KG@=>$@BY,9S$FPO8 +MV':9-\Z0?6(E!/L).&KJ+YFA@ODB#_L*^GHW_;=T@GT&?2;V;T:Q2/O=.$;$ +MYQ2N^^#9+V[!OCO&/6.Q%,=LW$`'I!?215(G\GXEU+5+G)/R13W?5'DM;4%X +M!^S,&GR6`VU/S83Y'N=>:[\.ZMVEVV#%>:T)<0IP9Y`?X;Y%;,,$\W4<7(D. +M^9_YC3-_S725?Z1WI9";3!@_K"*:4_*9F\2Y0Z3US"\^`7F1U)'N0NLFQH_K +M4F0X9GBW2:R'C9GQ4P'C@1:Z#L8YQ`24_!F_7S`^6"+VO7F@`-^]>\$Y.7L7;-6DM@$> +M=SD(WF_0/(7P]S,=K7F4_?^>YF'@U1-$`?4%((_:^:);?O\;:*\#KG>!;=*` +M^$9>CMU"IO67_*+-SK?03C7BGM/GE]<`'S.N7UXAX;``\!D![KXOC:@!X +M)=R_+/:]2H3_J9T.HJRVE*/^933X9;/X[DZQK4_ML&C[L9C54(9Z<"K0GND^ +MS)LCEJ'L\#;N7\GX$.P>!Q_]LMY%/Z$NKFR%.E"?(B^A3D6_`>K+$N5'['-R +MB#.OW/\X\,HMDKJM[N:Y.JX#J_D[R;/$L>WB8Q/O$6\Y#&\3)'@$O-VW281O +M0EWND-?D#^RR/%Q?W/<"RH.C7\E;7/1%DWM]D;Q8'&^]BVSLY'T7YQ:@0ZMY +MD,TKIZ`,]2>V+9V#:CE>15F^;_YP.MRGE;DOSED6DZM$6;2W;0,:Q4O:K7=I%VFS%_'`QWC? +MW\;??8#-Z_2[9#][F?T]T%OOX7OXOEU_H>ZRHFYB[3TP4^1QYC?8[2*+7`*+ +M\9V93GN@DNNS!_+Q748_T)4YY^@W=:N(HK8']>*=>;R?#QSDXWC@+38.KE,K +M>5_NS*L=9,\^QK'@LPK),XR%"\^8[^OZGF#)&`9?QW,H!=1NM>O>E`6\GRD1 +M=KT+>&E`W/2O"S!9_F7Y/N8_C5247\=W(QY'R.//!OTL'W*8DV7$K_1_E +M!]I7XCWC^VSB!_]7(PSVB<\1#R2XVM1'Y;,/X+S$;=79[XOK,$2WZDV8%V81 +MNXZA_:L,V:_X:HSP&[>W%\R:Z&3'0!E(^N.6=-01J=DBOJ)K2W-ZK/W +M&?5F[T;[VL;L5]%6D\ +M5]V9[SQ7S=XT7$?.3AG]7#4[%O6674:* +MO;TK[/A-G2^U"VNVTJ+^DM1XZ1C+H*Q"7$-@N&5UI2KJMCJ/C8\C-0''@3X1 +MM#^`=@_8?/#_+[+KAG@^J>U<@=2^NK,#=:7H>S:#O5/IE,/PWC1-YJHER]<\ +M>ZMFR?(E:Y8L6K9DPZ(U2U8LCWMAT7-+GM:L6[1:$Y]U4U80<8#>H5FT>O6+ +M+SRS6+-\R=.WK'IF]3-K-(M6K7AQ^6+-38NGW!1_^V+7/(A&X>QQ8\59$A`F +MRUP5EK5D.5_/2WMCZA:2"<^#ZWZ-N$[=#;\*A(-QQL+_`=!W37])VF*#_"&V +M!@FXB(6Q:>C93XU@LV76@>U5=Y:$5ZXE"KB"X5+*H3YX!_3\@SM9[&;Y@UI3 +MX)(D6K0DB=/EP9OL:[RI+Y-4Y,].^8-L?[8">!G73<4R#G>6**"/2LQ[@KDG +MP+>DJ9M(&/QV8Q]A#!UB_P_7361C4?27/*BR]QGZV6&0IY5"OY38#W=K=KB^ +M!?U:)*P%/,7P/!`5M5S>H/]5=?!_>0\)+L-<;063Z'ZSE>@S.DC8>FJB?SQN +M%/M;BKK@/Y9V*'`,,#<$U_5X7"-4X!HAW2&UL1YD>@+MJ]X"7/-[\""T/4VT +MAZ@[P%XWYH##=SB]YRR"=TR.^N +MH\7W@'=-TO9$7KO#A=<2?[V5MK6"EX'TK>@CZ,>V3=E"M'5;V1IB.]35`FTW +M`4V4;.T3>+-<8.LC+8S.64:2.T`IY@'`7(5`TP"@:?#4<**E)7,RX9YP&L^Q +MVFD,[[9C&\`+08UKDQ1\O?:AE4AS^IL'.NAUCW5PO#VT4C_`<>+`PT,S['@` +M_FZ'=H)"V3S1P^K`/C/?=1/@A,T5#\7C^R]=2\AF&%MOR8/3B +MZYJ\"WZ0EQ6TY*$JY%EX1V?/I>>.EM"&0K&%!&`;>\,9O,E=&ZY\M1Y6G?OW?2T<[Y4EC.U9.ZTUR82!>_?W(7N^B<=$\!L-L@?47GJ +M$U^OGC<3]7GL#/&@WR>3-:,P>)GXK(['7Y58^^+H-\[D&L#\OA_PXLP_>@7G:68V^F +ML[QZWJ>8][+S/L6\-9[V*6(U?.[6W8`R..]#?17@`>7@O\C'Z^KG`T[=DD7B8TTRZI8-@+SV\ +M*[B+S?.FZJVT&^9!8W_)PS"GSF?[2Z50!O.@T78LG7Q2WT9R7B"JJ=TD?NHR +ML@C;R#D#>%^/:PBLG9I;UI#XW)-POS&"3.DF=V-[B$_V+(L\PIZMB\#S5;E0 +M5HWK.[=DDG^5]"<:^P/W9HZW1P+CNE#'/A)0+;#QL78UN1'8GA'N[[:/RR!_ +M.)BUX7@>C.VX//_7O5$>=:X2]2'@K9OMST"?.&\^@GK1.+4><[N2&SKA'FG[ +M6@S?.P%<[1)M*W/OQF20B4?6&.2/+G3LQ3SRJFA[F/E\\S?QGX%>)OV([W5S/L/50C;T>Z#NNX<1#>2#T +MMQOJ"()VND5X3:=\_C3[^X`3.0U*USCO6\!S=J7O%'_W.,K8?;OXV^UB9]&XWY4'])?,5;LJ5 +M4![EIASLF?G37,N!SMU0GF(O!]H81=J\S=?3Y^_:2^XA$IH"'1YAZT9PCW3- +MPGN@L9QROPCZ^DBS.)^:X=Z(N9^@K-'3O(VZ!?E"I[9BNU^M5E$PUA/(ZAHL +MFXYE+?C,K8Y5X[K:H]/1CH'?F_1Y=P"?/@KCG]_$;;!'V9DI7".&\FG2+O5?#+SH0` +MO,RU3O87$/XQ_IS[_P-S6-T80"=Q,@A*P*29&%#`?RP<'@8&(;#Z_T``$8+R +M/X`Y+'#U<'D*`-2]8/-^,#!"[;-`4N&"1_=_!I@6;``T@M@0P@HT+P*L^/__ +M/U#S8?S_R'R0""H?")ABD"R09TX!+Y$4ET'(+Q!`LY453/*#B`J@>1I`>:$* +MN"SSET8DM:T)DH$<$EL>0@GC"T/J`AD&3A@3D3R$SV!3B@A_$1V8 +M$'9YB1^H?,DEU'+N<`-2C=`,(?]!$$,270S$9U.`B@D>Q%"/(<;H`"06`-/_ +M"LI=.@I&)&!D8!YH)PQC\!]2A`++23`%@O_A!>=_J,1_J,!_*(/,>AQ/Q0L' +MH'7ZH+E"8R"6AHIQD6`#$S"UL`!K5#8&=@8.8,V"56\#@X,$D`"B``8',#]A +M5)RNX@>@XA>@X@]&Q>DHSLC(",00%@,6@"[*"`5P/@,!_1CFH>I'!R"WF4"8 +M]<"V';@-\1\"_B"K@Y0[]S,8&!XL`&(6!H:'-D!\@8'A";"]_%B%@>'IF0<, +M7R9`E:]Q#?%0,-0ST3.T4-#553`R,##7-S#5!_(,#*V,+:U,C15R*XLR#0W2 +M4Q72,HMRRQ.+4G$YD3*@`,2R_QC`(8,:"JQ06@.*$8#1Z@8#HX4"`Z/Q`P9& +M/1\&1O44!L8P8(\G1(.!T?\"`Z.7"@.CLPJF6'P$1*P2J*<$*);/PL"8?@(B +7UES#P%CW@3:>I!B`0P8`1^0I`TAI`@`` +` +end Property changes on: stable/6/sys/dev/mxge/eth_z8e.dat.gz.uu ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/mxge/ethp_z8e.dat.gz.uu =================================================================== --- stable/6/sys/dev/mxge/ethp_z8e.dat.gz.uu (nonexistent) +++ stable/6/sys/dev/mxge/ethp_z8e.dat.gz.uu (revision 170009) @@ -0,0 +1,1089 @@ +/******************************************************************************* + +Copyright (c) 2006, Myricom Inc. +All rights reserved. + +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. Neither the name of the Myricom Inc, nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$FreeBSD$ +***************************************************************************/ + +begin 644 ethp_z8e.dat.gz +M'XL("%364````V5T:'!?>CAE+F1A=`#LO7]\E,6U/SZ[V81-&LF*$0.B+HKM +MJJBQ8LOM"S0*:-1`HJ5M%"0!`P;DQQ)"#&G8A`5[`P*)BC1*(*E@;ZRH\1;O +MB]X7U<5$&S7)QEYL4XNZ8?3RK.L:LY0$S8]LKF+7*R8>\Y?VLR]_/JOR\SQL\R5PGF=4;_(*Y +M5K.,MLK/V8:QP)?.F'LG'U)QA\V<54V@O+VLJZ&7537$YJVZC&5TXIV[65II +M/0^WV8,LF+*BOO(%9JF:R,R=11YF_S;1DIQ%M&Q:QJQ[EWC,?,O:^E?.#)DW +MGF"6UHR9K#7C*//63V?>0)!M7\926P/3F'=3F!$^[_)I>)X$3#^SES!VG"5G +M[@`>G\1I&G0G3SOL9(SD1.]&?%QI8[:GQ_)^#AHVXMDXEO==M8.E`]Z&_)4^ +M\R/YE+\6\3?N8+8#+K^E=B?O3RMG)GL%2TP+,A-X6-B(N*O219X6']N\E?+P +M2^;]/7S)/'_XY_.^YC^?-Q#Z^;Q`U3IF#5TR[V274\C*UN6$K$+,NOX+9ENP +M&G44_)BM7\HRPLFE>=[@AVQ!)>/X7:BE_?Y9/Y[.9M\^8SK+^\G]^#=C^DV9 +M=UV?-^/N6==/O9U=/X/E(%Q_-[O^/ORZCUU_/Q[W:_0"/*?RLC`#KP%^-IO9 +M722[U)=)?R"#P(`[Q0K^+8/N%.*_G'BQ7TDP*4=;FQ@#_P&23T*E@&E29<13 +M5G@"/RU.(!R(/^@S%V<)V0&^K3*+\;W9)HL=[7E'N!4FX@ +MMMQO34#:&,(CR_Q6.NHXA])]+*59S2_S?BM3EW>^H%FFIR24"YBY*LV4/Y#L +M8DKYEP!F--)+=7PW@U8_E0^8;P/&C'H?377>6DGM\5O[=&4>`MPUT"D3X>?) +M:WM(MY`O`;#=.MC7"8[2D>=ZP@T8'?^I5K0CB\"U6>(";*:$3;7K8*<@[68? +M^]8!X#5'RTW--BC7K.#ZOH)KN0Y7-=)^@#JB=IR@P;5#!V>B=,C8K_)KU-9. +MN],/PQ8]?YH[6-#,K/C=%*QR,%ZSHN4ZC%"N92S9&SS``4P'XYM7 +MM)QU,P;:/&=Y<0)H\YPR,P[=[O8&BUA!R$PPGK-GD;9EA5?`I*SH$N\U*WS< +MNJ+[^FHVBO@(0Q^]0=BOX.\B-L]`:RQ1V(?YN-#K`TGDRX^PFO +M#[@^EKA6$IR?T@F7BH=P#-[*.<%[@Z7(LY*=W;S"?W;S2AM"%M^R\GZ2+T]9 +MF1?\*KB)>5VR5O*[=1 +M/-A-Q.^6D$Q[%?GF#DKZ6KAUI2?DM@,GKR;Y0M[5P-U"NN`M#S*>O-(#FKJ1 +M_]A929/_]$\%[J^`9_X@E6M=Z>LLIV1M"U*C-L795U(W.:M+HD9"]T +MZ&6*LY'>D,Z0OA0$K2QYO\O9 +M1CBJ$!<*UJRR!:VK6LXD,>1I(+F=33,%$]+*JQ.[`B^PSN(#0G<[^W,0`FS. +MR6#:0\5L-."^(AWFHZM9SN=A/N?D6=Y1N!7P6TD6F=27S/YB`/&#O+/X9=91 +M>`!PGK3@YE6VT.95]G#RJJSPYE5YH>153<'D52U\B],.V24+^:8TS[^QF9Y:WO`CUZ,PDW>>;G7F`SU/@"TD_`.-$J.9;5C]_FMJR +M=743KRE!O36(]NHM?P'R979JK][R4E:P;GPUWJMY,N`VEPB[PE-*[T!KAZZ6 +M9IW=7%J(@+J@_"\H.EZ:I;4GC%E@\A.MC"4AC+*J]F70?>%D'_M5!MDE_)Z* +MW_W*[YD^]K3PE\C6.%WLXA/LPJ$0VCE^I^)W,/>:-WFXIOD@9#U(-H"[I\"N +MLZS!P6(K'W2D\I2R)'XV@_KJ-+R/!MV3\!RS^Q1+1\C8[0Y74S^$WYG\L8R4 +M[65L65IW&^QL$:2QC9>$>C +MF[>$!AUFHE.EA6@#G9>=8&-2?[0NB[U=S@Q]LD'WF`C_<=)S5)G$25^NRBF. +MKV2A]A4J0U^XC)$L,JJ"_'\AQVI7B%V6N^X*[NWG'F_Y%ZP1Z:Y*'D9_U3]0 +MQH.[T7>&D]?Z44:WCUW5(>MB5>8VZE.M:_N!ZT+"U8G\H+H.L[5''F.(L&D?9AI!EG*SR@K);!N#S5:UG$R#;2T^PBU94E?/CW'V1 +M96_%$/K"B\I];%-<62%_7O@4[ZLMB\CAL]QU84Z_O>4AUHAXXAMX/"J_\65: +MIOA@Z78./]US>9!^3R32T0ZS!M`>R4>$OQ&`3S\TZ$Z'_S/>J?I6WN`L:IM9 +MZ0CV76B#@S +MNQCO'1$8X,#[44V>'PV8K_T5XORQ>2YFL7DNMJGOH*_?7D9\C?T`^9L0JF6Y +M%T_1X9BIPS%W.*T7%^ORE.ORU`RG]>(&79[]NCR'U'?I7[),Q'7K\OAT>?R: +M]XL&W6,C_%-=AU$/P$-Z"QTDNTQ^P$FVII(/00>`?VRFBE_5'0$O]:=/ZL_8 +MN1$8N#-M]@"#+/M0I_W>P"R&<0W)M)YT2BTSK5+J#8U?U#)]T!7J$S3ZV4ME +M*64<5,L`[E[2'S%F`WXE7Y:4^UA?K#S&^F/E<0G3RG#C3O(Q+VXC/39LE]95 +MK.HTN[PJS(]!5Y^!WB?0$SPDY`Z!7VM%%HUWO948FR8+V(L`VPN8QQ58>EJ4 +MYRAZPE\)@>8&/_E`5A=3WSG>\9S+-U<8MCW0?DBUIV'TB:%3O+]V)>KB)+L$ +MON$5G>P8ZZS\8W757]E%\'T&R6Y@3#C.6WT,R(ZB; +M:M!K/LXRUE(='&,9A<0/@@EQ]_%3?#XGOQ;]M8]E;.749U-PLT+EW0FX+'XJ +M3##5B$-?92(;=@AE;_*Q;]?)\5)&'N&*0X.3QM%D1X#K"[O+1'0<%C8'N!K- +MLH\,@]8][G`Q^L/B\);*J>*WDD9TA%&^(?Z(#1KWK&+'"L-69[5B?_O`:_(Q +M-JX>[V9N'C?ME;#??+A4[0+&U1N$(@1`C*M6WK<*'@:*,&X;5Q].AGQJ2%[C +MEO.!26;U=^/?:0PYKAZRJ(];'Q%:Q_](0ZM30ZOU&!L_5Z&U+Y;6\7,1T,&- +MGXJ0K;QK0^J@>WP&RD^7=4)Q*(-D\7=F#9_-9S0'0_$D3XH/PW?!^V2DVTA7 +M:L-2%[Z)7T5]U--DOX0]&E^GLVGPD\;O4]MPZ42646KG?2/,M02>1AO&>+J_ +M=8+(ZXOT930/XY;ZT\LNO85XHGZ&[!;Y2XWXW5I.>2[5]G]DMP('*H8LE)_F +M8V3^\8%X-D+,?]2X'%T-\,,AY.491K^]E3]@7L!( +MFWCI@;9R.?_1YOR:#5`=EY"N7_I9:Z6?Z-KO,Q>7RSJYU$?E#]2X['N(!^(W +M,)5@8/_''U3YW2[&S)?6C$"K3=K!"=;8/BL1MG/"A%C;.6%RK.V<,"VVSS*C +MSYJ0I\M3J,OCU.2I4\K9I,NS0Y=GG^8=/$XXH-;/@)B'F]"F>3?37`KB>M0X +MV&8&GR-`.J'@"^AT#/"766-IN"PCEH;+')%^$OA17V\=9Y>+]D3S=6AON?0. +M7/6`AQPNR]?A*];AT_;_-KS7:'BP)60P$_2E#_%-:CSY2Z"WKTK,@6;17.N8 +M8^RR;>1;*3BU_@]3^-+U?Y?I^K_+F8:O0-H&=E]:\/X'R/<"7W[R@>`+9*!, +M&\WO'B^EL7^M[$,_VD-C+XO'=8SP%"8,,>O`9I?=A]^AS2Y'N,:5 +MY6.7UT#_#&V6H6TWL[1:=[C'&PX0SJVRWUKKX^XW\]&G&/,OA#/LWC.4G%Z7`(G<)E^2+AT\4.H_S-IP>K\X6GV=.Y.N-\@?C)L +MZAD?LQ<2C9KXF=P]>B[!^Z@\Y#'NT]?ZUHQC&:#S-/*.%M7S@YC +MFNW[0?/I.#2W"]F90;=Y6!KYC*>%7&/C@U7/H@\S,Y'GAY71^I#I$R>DU;.\ +MX65-G,+=HW[H8Q.7"QV)QN=(&B`+,\D"?"!?O'I4^T*2PZ![(OQ_^W+5-X +M0L:\7)5AS,M5F:`Y-%R7KLI&O!%\(7<;XB^'[P;[?-5^\N,T\771M@2ZD8=\ +M/(_KNXSH.6YGYI'D`?Z_.LXFW1S:+R +MAFR01[/^.6(8N0ZN_KLB_P]CZ;C::2S_JVOBR__J?<;RO_H0R1\\^6/M\=5' +MHCQ=3>.H-.Y.NHC>OREO:@!^4^-S\._=IHG_2/YXH6Z\\;PCQK+,?CGY"=]^ +M';K3@]\8ASH>"KO-E8IL/SW.OG.3E*UC>JP\OMUA+-MO]T*VGQK+]CLL?K_Q +MG0G@_]/A^OV=*3(/VTAYY'KQ=UX@&*G'W]E&Z:3SFCS%P)4?U?7O%"DXM'BW +MIOF-=/H[^P2^@)!/_AXJLXPQX#A-B";AZ\#OGDHRZLI@-"=;=9Q=>S>-@Z6LKDO4\==G+*MK +MX:RQ*F-973O!6%;7PO]A5&=C_:Z/V +M?SRUB6N%/\4'8/_KV2CJ"Z)]0*9!'W#M(>,^X-HCQGW`M?U2CZ^SQ^KQ=99H +M'5_3]W_4!]0"[Y_0#U?+^IB\.9:6Z\J-Z^.Z.M1'K7%]7+??N#ZN:X/<:X?7 +MQW5'OUD_/-D2VP]?%_CF_?#D+.,ZF)QO7`>3G;(.)N^+K8/)-=$Z@&S^R3H( +M4QV4L<;C[/J%>$+^-UZHHV/(6/[7VR#_1F/Y7S\Y?E]Q?3:E0G/COEA +MI4_GSUZ_7)45:'H5[R%)VPTG=7`[9#FF'QJ4TP+Z7HU#7X>Q?ES?J_0UK_)D +MO8[<0.NF!G*X(4/)DR]H@/[4XNFIH'G(&T3_!SJX=RA(^I!?BO&*`=Q\+1RM +M#Q%LFSW$#&!K]#CCP+7$PHWZH5@O0#K!ZW@X*G7MQLQ87;LA$-6UZW?$YKG1 +M%MNG31[Z/[(';P+WY](69/Z;KLQ-QKIX8P/X?-.XKF\\:%S7-\+_96_RB`YF +MC=*E^XW;:Z;5&%^F77DEZ>E.^[-\Y2RN/P%I56!W.3:3/*IR=1=J9W[#>MOX[P>IXM=?H)]5^S] +MH_?1&TRT7\/,-S[:DX;?C6X^5&OFW;5NWNT-?DU^RD7'V'?72_KXD)3%=TL) +M=M#]W?WJ'%>\\F@_#N$>:6X%>`*00;8Z_SOHOMFBKGWAMPUI!T=83[>J:PR` +MG:;%LU'&Y:EK6?@]_QRX%'INWJ2C9P?ZN[D*CJ:1<%`]2?VB>KSYX+GF1FC. +MX#B;\J)L3U-T_L?-0W+N2_8Q"X)Z?9N2D18PTK@I8NS`Q\AVA?0\7;Z:6+V\>6BD>43)VRVW2]Z^IQL73#ER#MX"QKS= +MDAK+V_=T8Z5;1/\_,*P/O66FZF?9KR3_Z99GA+_56,`GV8W\K5M*(_Z6A)=C +MP4;X6TUZOS>+M0;U/M2;KW +M4907P:(\$W7O2>=('Z64JY9OT;TGGB,]Z9_,SX:]QZZ3WKVR;.'RI47VQTJ6 +MEBZV+WSXX<5KUMA+5]GON/W^[_[`GEE^=?ED^_+I5Q>E,!6R9/'"HA$!M7NU +MIVS=R=BVG<(6]7+9T8X?,S5=QS):VK3+V&"+VAJ9HYE]&>MJFS +M^!M^9E_"S,?8BKO;H*WT[NT+,GL5LQYGN9\AS<1O#2,NH*S93WV6XL3ZJ/O[ +M]0U/,6O#3GYT]QAF:7R*UO6F;O69%XNUJ*V(][$5V80791??6&UF6\N89>,I +MT+>2<<35M1:'6-47M+X]M3UV?7MJ'6/3CB#X$#"^FP8]FPZA3H?_.AWM=3KT +M?3I\\^GHCZ?#!DW'^&-Z.4(U`FS.=.2?WH``OW)Z&P+ZS>G`6^L1(+U;FQ&0[]9#"-T(\+-O1?FWI2(X$&8B%"*@S-N0 +M[S;DN0UY;CN(X$'`./XVY+L-?LIMZ)>S0'<6Z,X"W5F@.0L2R4*96:`Q"_FR +MD"<+>;)`8Q;XS4*;SJ)\0<9N1S]_.^B]?2I"#@+HO7T3`LJ\'33>CGRW(\_M +MR',[:+P#XKL#/-Z!?'<@WQW@\P[XZW=`-G=@_'$':+P#=,T`W`S0-`-X9T"& +M,\#/#,#,`%TSP-,,T#8#8[D9H&T&P0/W3+3XF8"?"7PS`3<3\IH)?#-!]TS0 +M,!,PLX!W%LJ?A?)GH?Q9*'\6X&B_#M1_IW`?R?D=P'_7?!A[T(9 +M=T$V=Z%.[@*>NX#G+N"X"_GO`@]W(>]=R)L-NK(Q;LA&W6>C'K(!EPT]R0:O +MV2@G&[#9*"L;]9<-GK+!T]W(E',/ZOL>Y+D'--T#>NY!G=V#//>`EGNA%_<"[MYI"-"I +M>R&'>P%[+_BX%WS+\J:U +M/_T<8S[8@(;QS$+?#=%OFK_$;[_RVQ3][:#X(27>3/#TW1'!B[A+19Q?B3,- +MCW-$X.@YL*LX@7"H^95WOYJ7WGVP2?))[P[DFW9$>?KD)>E7Y10GS10_S<&T90B^ +M-303O9IXOR9>(Y?I;2(]!@_)8WJ#\FQ1GJI\U/I,5,HB.2?JY*.-,PV/B\A' +M"Z?*QZ_PF6@@'Z,T4_PT(9](&1KYB#@#^:CQ&OG79K3Q; +M8N7CL$3+7S=IOR[E?M#]&BL1M)$;OA +MD^U?M7/A72+-KTLSQ4]S:/,-Z?*9U3278D-(QBY)-]6Y.7QIM%R78D\,8$SG +MAM'0H>%7M<_Q>`YKZ\&`;VVZ#SZ4:M>B^*6=)_Q(GR3?[\A2GC:U_2KPB3I: +M_$I9B0;RUZ;IY:])&R9_;3Z]_/T:N27&D;\1C%[^!C`Q\H_P:R#_&)X-Y!_# +M=ZS\9QQ5[4(4OU;^,_;)]QD>Y5D7*W^'7O^5LAQ&^J]-BR-_AY'^:_.-(']' +M//TW@AE!_@XC_8_P&U_^CGCZ'\-WK/QGS8^5OT.G_[,4_9^EZ/\LF^K#!2\M +M'D7ETU-K/S7OIMAWZ<]ITLW"3BIQJFPC]GUXO,DXWL%4G*J/IZ3'^'E*G,;F +MWGF0GM&\Q-^=],>-*EQ?"E3?-A'*;U#57]4=(4N=ZER/.N:K7]JK93 +M4W:,OZ4I-\;?&AX_3)XJO%Z>,;Z?@3SUZ7IYZM)CY.G7VD8CG@SD&>$K5IYW +MV[2^9*P\LY5Q2/:0\NR.RM.AU\\8_RRV7(>1?BKQ1O)T&.EGC*\87YZ.>/JI +M2]?+TV&DGQ&>C.7I,-#/>PJUOF>L/._)E._W9"M/>]2^.9*D+7$DQ=JWR+LI +M]EVU;Y%TC7US)!G;MYAXDW$\V3>'UJ;W*>DZ^R;B-/;MWGIIWQP:?_+>^ +MD>QRFHSMFTASRO<<19XYA3;'OLAUJ +MTB/MD.*,VJ$NWF0<[V`J3K4=*NDQ[5")T[3#7#L]HWF)KURF/,6X[AB;FD_S +M\0/F--Y:/(6)N?>=_,B>G;Q[T)UG];&U3/GVH7L;XEOMC-%\>]MRFIO/<_B8 +M=9-^;IZ[OU?^BJLGD=:VNOK[&3=_O[SJ7]&7Z`T>8T_OY`'Z;I/62YW? +MIG6#O'77[6`V9=U@7%NE?MU@^;O#UPWN7RKBWN!!@N.WAH:4^-FZ>+^,O^]) +M=9WA_:,>T/C]MVBM05UG:!C+?;O'\IY=.WE?'>A""`RZ[[-&UAZ0O@WI9N3O +MZ/,P<1Z/B3G#YDRV/-8Q@#+9:Z,T5)UFJ?0-N,?U$?$]'3+UB_,$3O__XSP!DC/)RL?N +MRR"Y@O9#5"]J_#/B.[1(VCY*HS4=;W&(O;0L9-YRAEEH7<<+^$8!A&\&'X&?L)TCZ209")D(VPER$8@1HV$\V(=0AU",T(>Q' +M.(1P!*&?L7P+P@2$:0CS$9`G'_#Y@,MO1T`Y^;T(!!M`"#+V0"K")`3D>0!E +M/8!6\L!6!.1YH`,!=#T`N`=!TX-H20].00#L@S,17`?`NAY +M\"@"\LP#+?/`QSS`S0,/\YP(X&$>8.?M0&A`0#GSVA"09QYHF@]ZYD]&0)[Y +MH&4^ZG<^X.9#5O,AJ_G@=3Y@YX./^>#C(?#\$.A_"/0\!`U\"/@?`LZ'`/<0 +M:%@`NA>`W@6%"*!A`=(7H/P%*'\!\"XX@`#8!2B[(!T!_!6`IP+`%0!/`>16 +M@'(*;0C`58CT0N`K1'F%H+$0918"MA"\%X*/0M"U$#PL!-Q"X%E8CH!R%H+V +MA<"UD-*!;R'*6PCZ%ED1(*=%D-,BX%R4CP!Y+D*]+4+YBR#/1:!O$?(L0IZ' +MP>_#P/\P:'T8^1Y&OH=!Q\/@[6'0\3#TXF'0\7`?8T6`*X)LBH"W"'0609Y% +M@"D"_T7@OPAT%;4@H)Z+`+\8N!>CCA<#?C'P+0;<8O"U&/@6@^[%H&$)8)8` +M[Q*4OP3E+T'Y2U#^$L`O`>XE*'\)X)<`WR.`>P3E/P)\CZ#\1U#^(X!Y!'@? +M0?F/H/Q'FA'`XR/0S4=0QB-#C!4#=S'P%@-O,618#'D4(T\Q\A2#GF+(I1CU +M5HQ\Q!GF6`60;]6H;Z689Z +M7):'`+J6`7X9<"]#/2T#_*/`]RC@'@7MCP+?HZ#[4=#P*&`>!=Y'4?Z/Z0D: +M'@4-CU(>T+X<92QW("#?#KDL!Z_+HK\2;C.,=,?!G"Z@_ +MCJ5)I9/25+JT<3[8,7J>+7"8HWS]V"/??]Q-3^([@O>(P!N1`>CH4^-`"]/$ +M^S7Q)N-XAQ9^2`,?*YNY\(5)9@7PA2$WO:P-TDTCISN&\1,C.QU/,3+4\14K +MRY_T"Y@(3JU,?W)(OO_DB/+<3W(%+-5)HJYL$6<@3S5>+T\E?I@\57B]//V* +M/!+CR%.?KI>G+CU&GJ),`WE&>#*09X2O6'D^,$V1IX)3*\\'4N7[`Y/D,S\8 +ME:=#KY\BSEB>#B/]5.*-Y.DPTD]%'HYX^JE/CR-/AY%^BC*-Y>F(IY\1OF+E +M^>#6J#P=.OU\<+Y\?[!4>>8H\B3[DZ0K6\09R%.-U\M3B1\F3Q5>+\\A1;^2 +MXLA3GZZ7IRX]1IZB3`-Y1G@RD&>$KUAYSCNJR%/!J97GO/WR?5Z;\FQ0Y"GX +M4^Q'DH[O/M<1::>(Y_"G,>E^7;IIY'2'/O^0+K]9F^Y2;![5B^M(A#YSN""6 +M#I=B_PS@3-\,3D>71AXDRV\B$WV?9B27V'IZ:*9JEZ-E.'%'LR +MOU^U)TJ>1`/:_$JYB7'J2YMN5%^:=,/ZTN8WJB^_1KZ)(]27$9Q1?1G`#:NO +MB#SBU-MKP0%93PN4_G1!0VQ].8S:EU*N(U[[ +MTJ:/4%^.>.U+F_\<]>48J7T9P9VCOASQVE=$'B/7EV.D]C5,+K'U5;@OMKX< +M!NVKL$C64^$FY2GZES.?TCQUE!YZU]M[)C];I5DO-Y7N6%DMRJ2GQ*/M+Q99Y?NB"?*Y4/5GA"^IXJ!\\?Q3 +M)YM#GB_&_8]+B^.`Z&$,__-PP.CHT_OBY>%;K(1[?VG0?QMMZWUR+ +M'^F94NX/YRC/"5K_W("68?ZLAHYA_NSP-$/Y1_QU`_D/\]D-9#O,;S\WS##Y +M1_SW<_%L(/\8OF/E7]2G]^5CY5_4(N5>U*$\&[3^O#$MCGCZ/\S_'9X63_Z. +M>/H_S,>/+UO'2/H_S-I[?/`L[`/LMX8_L<23.PSVJ:UCX[8GU[#VS +M@#F'?8X'HZ/#T#X;\QS5/V.^M>D^MM0:WSY3>G&WE'MQO_(\9&R?([08V.<( +M'0;V69]F*'\#^QR1_PCV.2+;$>QS/)AA\C>PS\8\&\@_CGTF^2ZKB6^?17J> +ME/NRY*>$H_6)CRJV()'A6X>8WEUM'Y(:T^1M4[-FM^@>WE`7>ND +M-'OV.<1N&9\SD;1FBGA:)TO\DU3SXN3:Z+W91A] +MWZ9^:Q5RLRM"=$Y^]4?,6\^KNX)'V()29@XG//I9K9NWWU@IOGEMIS7,$VS% +MXP4!EL0W(LW,V]/*6;(F[1=4CIHOK9*9-6F_I;3KOV0F_"[6T_./T;+RPOBT +MK+P^/BTK[Q:T]#.3?26[\$NV0IR9Z3VG&>T>77EO#8" +MWLYSX/TB/MY5R;0."UFEAN8^R&0$R>A7J:1@=5FD3Z!BU-2#-KTG9'ZW>5/5[]TGJSE[ZPFLA8 +MJX71FFK_CLAWRZN@_ROR-><$ +M\LSR+A)GIIHWWO?15*Y2IO]IL;;N+%+S$&XZ5S(,_1/G)`?[:8WZDJIU5!:5 +M4\B4$?@;X1R5^>>G +MI'WF^MASQN*=2XPVW4?GR?HW5^3D!GD8]@]]QQKH?^D1<^H/2//X<\- +M#?""D)4UGB&ZU\Q5SQ.0?*R!_I<4T[L_X=HQ"`[#;]A5?,D5.06A5(%+GLV] +M9E_D3@-)D\D;O(4I]'A4W'HYZ_E:M&I5:4')8GHXKEY[38JN+E*I[KEYC8_V +MICP]1CV;H-2N[HNALR2AS[>1+E.=(VW:X+Z&R'@>1]5"\F +MOOG%?.0K];$U@1'.H>P+;7'5V<_.N*'KH/R>3";[*K]!MT<*+CC:][ +MS,=9:3O?O+8/]>*)2X/FC`DZ)Z"7@82XYXZLGRKO,UA[2QN=HS6>_?LQMO8F +M2P;+P.__P&]Q=C2EQYM^=$VU/"]C\]XF(UJWN;F3SC<. +MIS1W5ZUEYE?/^,RC`^AORYB9[ID!K8??1!SX+^XC//1 +MN<`#*159@^ZRFF3S;TLH?,H=/\:ET;P8_Q:<0+-XSZ1W/R?1,V^#9G19\B*$=YE/; +M0_S(0T]+*T\ZYO08TL+9OU3]!`M +M%#?H+B^/I:<\4O_:.J1;Y^Q+U]C7.E-B;!7@#_I82)R%'J9[#XC>FK(,.BLR +MMYR)^U&$G=B,N.2]A0A-9$MG75/%D3<0L:<)\_J4?":Z:Y#.,RLH1[-%/IY` +M]G6=W2B#SK]9+LO(`.RU]PO:T2=PT2?X&?D:1/L"\M?A7X;11Z!M%[)O>#9' +M.*5LQ^@--K;'',X?'3#1/0KY=>K="4BCO8&-2$L+FJH;-6FU[K"OUAP>@GVV +MD5TFOZ!1^`X5A:I!AF6#M(=$Z8NQ,UQ\C#==Q@F&:)L]+.%3A?Y +MR163B':EGE-)'R*ZL:4\/4#=$'^WK1-\W +M5MS!(_K'GV89\_?3A4;\<3/Q1_KS4]&';%?HI/,X^UG%D*!G0S7I[@S8MCJB +M6:75J+[D&4F$K]*"?(6@ISL./7\?6=X_?9OR$RW0)6M:$!TLY`0::HS*'6UJ +M,NTQ&Z=Q:H\NZ;N@+7&T1TYV9-!=N5QM=Z,WV!.HG]H#'HUPI)GJZ(RL&6A' +MANFH-V5D(]UM(G=YD^X+F6T,VBA?AMTE1]GKLNH7Y'W`U3V^MCCTX3_ +ME5QFE3Y9)>S?$Z51'VV]U4[TD+,E!N$W1LC[G3)`%R/ZK=Q]_1DU*.XQ](,^%,W9K`! +M\/B,.=Q,=)W:.+[YWV]@EGJR&7IOAG>HV_8TX5^2-5 +MXV@LSB^%W7./DF<'AL0]F6GPS?VP#2?)]T"]G^RJ'Y+GUI31N36NSXC^&P+5 +M[(82.D/8M:^UE/;1564(?+ORS="92UUG +MK2Q=7+*XR)YS4PJ;NW3%XJ+K5ZTMM3]6O'BEO63QZK6+UY0N7?F(2+UCX<./ +MTN$[.9F3[22=@OMGS?AQP8S<'\V9*X_@T8>ZN\LZ6J(7;/ +MDG@U*'`A;%7JW@]XVE5X?XE6[`-LW^@2K.IEFLJ-O?#$/;:F:;(F\ +MQV?#=/CBZ%.J(0N7N$]6WK59+>P$P=/=/L=8M0/]=U_:!CN=U59#O]4Q3YJ) +MAV$S.8V=R>87!+?0.-I",'(<5#U?,PY*P?ORB(\'FRWO_JFN5L]+@]UHWAZ& +MGU#&]]>6,+JKZ%*Z.PQU^[_RSHF*;&WYN2$^"N4U*66UJV5)?:N:!#MF)IU# +MOIE([U/UCF1$ND=],[N;=QX@/EH/\.PY?@=/@-9N5L*O=G;1.8Z(^?-H>+GW8CX$D^1[3OYD.21[>O#66BGQF*-Y\CQB1" +M%S>F:NI+W&$4.L6/0A][T"8M--=#]VMM_YK1N:7'<]?=QFF.A.8&Z"Y"P%PN +M_`+`S(%_TA7XG/+W[/E2Y7-CI/\;^';"P8%38=;:\%WHV$;P[Q9W/0&.#6PQ +ML1W"M]C8H,:#AJ.U7],Y7?"!@)/\7=<*=CG=0Y(;NHW3W:VN?MX+/])/O@K! +M-WXM]*V'?&&:*R$:O.6S%+]EXY!*RR:\&\FF#N.Z03=+&MRXK&+0O0GVOVR^ +M.@,#^GV,_2P3 +M3_1!/YLH;7?BR4'WX]6J[<;OK3Z6(NZT'DB:BC;U>(-Z1R2O6>U7VS5]$P3^ +MLL*;RYAG)=U%M7D]W[S:SU-6U$/VJ=0WEW[!S!?TTUU5=";BXT.M]>)>*3_= +M.]7+'F^#;V7SL\#F9)'=X1^:\PY3_%4M&VJWO9 +MS\1:![5!DLD9\`\9-*&,%OM*=@'D_!G)@/J4/6?$N7%I=%])NKOJ`[F9N8M[R&OA6S;J]@MJH`'ZIR\B&ZD[?+V<#>"^Z` +MK:?[@FKZWR_?P>#[)^/WR_8?4UG_7O?^D=^S]SV_9_8'Q7M31_Y[K"/K/5;5 +M*VR,&>T>9=0`5Q.#+&WA@8P4\&^A<6]7`/W(RO\F&B?N^9I9/2O?$K_%V`[\ +M>HMZ:*P9]/;U,.I385G4_&A+2OY_[Z7\NTD>H*ESKI1)1P]D +M^F-%/OE:^?Q[%LF'[HX*EWW1!-JR((.P6@;5OS(FL,*6IGI6GB"=W)"69[&& +M3WW94GN26:C>A,T0Z4+F-5)G'Q?V'754@W+MJ)\F>DK]7$5\V)3[[Z+Q6U:E +M!7EQ`G03NEMC#V]>/U7<6^I^W$+?!-*W@7N>8JE[QC`;]?G/B7NW-F>H_=Z6 +ML70.5TV>6=YU:E-TVP:8:3Z6>E"9@_"K>NZ'GOM)K\6AKOT7.0UH([V\IK0JZ6!!JHC[#>"P)OPA]^*?27WC"IOI$ +MY^"AF&]9"ZHR6'B+NP6T5/%!![NQ@N8;GLA"_Y6EX/U*P5L\$E[R.3M@HR5/ +M[J80<(=2W#[8UZJJTRSI.-OZ.LIIZ@IVBSFJ/6'T,U]E,&7.M>I+]L2S&*/: +MX+O1^+V*;!CY?:X02_*6DIUM/@@<+[X]1/-Z:),\P_S;4SY&/@[:F97&FJ,W +ML-NX.4W,JXL@F:^=V$+]G6RVA^EUO7]C?0_$7ROCI_\MYJ +MCKH;=&_-\[$M^$#:TJV_]2>[JKE<2S6%RT(MM7]G%BF7K;N[^@<]O,95 +M36MB2AEI*&,%WTQCPZV'9!V)WV0#3,&SM*?@B6##&1J?/M',W5=X!JRNZA]F +M5$,'MW;[3%OEW;/*]ZEQY_EW9:1X@02^3/H)5GO[&3#:) +MY@@0YR@(V9AVGF#C>#8YMMQMJ7)=JC:+GH_YV>7.6^ENV>UTMQ/'>-:0C +M7<&O/%6E/.Q9]C7:VO8'/"7[66YO4*YKSF?^1N!K',]LP+78NUR,W:_TE!P@ +MW.O(YF^#?*3MKYWL'>IAG6CG&O2!4+61(]$;FAQ0^O%\+ +M?&U><*#RI,I:ZE>MR5.RCU']$#Q@_=Y^P'X16R\$`]B)P/M]FO^D-#D_%85I +M`#^05=$NR$B.%;=O(UI];/M6;^E'AO:;;`#T?D.7F*.K73\G&.;2KM5EPC=J +M\N%)-D'8E1OH+L*ZF]),LGZ)%_)+C.I8,P]E(UK5.L9X]F"8YA/'0]YG,YC4 +M/UX'O!,5'RY?Z;_R!Y++ZOA/,YBZ#H3VN[5`KJTYR%9A3-LB=;,N4\D[5_I_ +MM7[DGVO(;P+LCQOF&OQ0G\=AL^`7)7D#9YB\:['N9?BG27PP2MOYS4?5EB"YV>-/;O;)=E/GX\G2[A8,T?W=Q/O>)O*' +MB>=!]Y.HO[7[95\L<<=;%X_P!IX$;^!1\*;E!RUWTE +MYIEKQ=Z;$\SN2OC+,?;4NQC?.^0<[U/]/K9?SC^!?RJ7R;45RZ#[:6MT3N=) +M,>*P_]^US)]U?*W)*DF^B7]FU%"_3LXA-LQV]#H)WH#A-M*:"1;!#- +MYW_E&"/H^\IAI?,L\$Q%?#K\@#S00W=LF?@JARV<["H.?^48C6<1\&>9KT&E`[_SX]%^_G)X)A`/U[:=XCR0JNT[ +MX2^B#VK-H'MX8)/W!=B:W="I+]@%WN!LUMJ0R3`N3>4#CC%/0W>B]9_`NP+( +ML\9AJ]VI^H8[?UL)/<2XG7X?FFGGP1WF<'&#.UP,N^%#_U>LGF,B^M1R9@YA +M++RMA$V&;F:F^:6/(/N!G73WMTTYV\2FG&TR!?IKD>D_ORP$'UG@+6&3&KZ& +M74/_6O"Y]`5HW#&[/XV'_N9@NT]$SS[QKW&8][AY1KRYQ=JQ8OU#[!EK'`-] +M&8A>YD:VN2ZS6M0?S.(%OS\ZWJ&3#&=F)?W8VB[]YWGGWFS_?']8&! +MD_3@'\-;;QFAO[2$:Z3/'T["6,0_Z)'SV_75WDUAC9]?OY#2*,Z+^L\2]5[? +M3+Z_,GYZTQZ)D_TXV?)&N6=L]*"[WJ/:=.K7Y/WM]4=\INT-(^QQLRGWS9OE +M//VS%M7&$PZ:_UZSCDWXX3B:;V=T;WN?['>>G1R=@\P2>E%+:>1CF]$'[11W +MR??+_N=KI?]Y]FY-._6,+U'6TW.=>'=HWIFNBZ +MRW.="BV=K4[&Q+E48^E*9"W^A)\P%U +M>*(?>E&,Z3&&RX"?;!U(?MX3;U\6Z3_-<7@W_9C\*Q/-M9Q@NPYV8AR4BUY^ +M39"?L5_.1AU#7-5N6N_;M35VO6\7VNPNR+\!?E(#)-*`]X8VQ!V$=%('W;O@ +M_SXG_(WS:[.4'_5!XX-D=YWDN[D=[U:>W#RD\-V,=YN]"GUR\O.4ELZ37RK$ +M>#B#Z`TG[ZO7[DTCG"0'?S+RT;VPWTXXJ,J5X)#/0G!AX)`^`>"!0X6/U`&= +M:4;C)Y6LC^*&T/<)O;3CJ +M8YDUBNX$*%_MUU2_**L$?*(,+A +M*I&4_#;4QVO+KHGPLQK`A1QW3?-."#!O0YB%M^SU9+FX +MYSC;_4'74-]Y^@V[9?]_IIIY*[_+J'_[DC6:O'YQOQ9^[Q%[WFG-C/I0C'&+ +MU3%NN(+V3R`><=Z`GVQ<)(UO>;Y9RFK/;X0O1O0&[_(0S>&:YYN)ARK:QQ0< +M]%";M)?1G-V>9Z#O/+SY^>;SXV&/F).H"O(O_ +M0?>>@X?+I4Z<)]Z#*N][%-X1)^8Z;JP7\PST[H_?7^YMHOX2_=)I*8O&++5? +M.C\Z&K.^01EGE3+V_8-E[!NI#+F&UY1Y_F.WQJ#0E9J]S=)F_^*%H)M9SM8T +MMXL[8,VLVGLTR()6=UW'$,;]9>^Q7M8D^H&.@!@W#9U-V6<[XVZD/63"G]ME +MCNI95^`]YIW?SKS!=K2[YB'8I?8P;%)5+P\)>3CA$RZB_J/YX/'E&//T*V,> +M.O?.O;9HT,WRZ!R\+N<0K4E9:.TFM";?GEN>P,4Y?<&3C,[$P]C&[`WTL-!C +MCC$-92Q]5YBEHZ^P4=_2:'`NWF[:(W8$OT_!MX&ON*L,?N(IYMBE\1/)KS$Z +M(\__F,.LL9D6JL_SD_DOQ!VI0>OS[6=K7BJ$_.I]K*D),IQ@5+_JNO@.C5P/ +M3Y)]"W#UZ?4B>^'*HE5+EOS`?O?*AU>M<"XL7;IHZ?*EI>MT:YNI-%;ETO_R +MRWF"YU>3+W9^O#P_3<[A/%_I,VV3Z\;P0SU7A@C?-*E;+V9!MZ8=8R]DDI\! +MV=5[*T^P[15DU-)G[P;J*GQ+M7[,NB?80*S#3EW>^UA&F\FBCO +M'MU[F.+E/JQ?!,F?'W3OG1O9>V65?27U)U[+*88V/4A]'^$D'4#^:MI7(O>E +M[H7_O[;F_&W:WJU2GGL[?*Q9RM-=)>;#:*Y_ATM\9Z'LAWAAG%BK+?N"YFZJ +M&\]0G:X-*KXDO5O#I[YHH754DBGY49Z2CUCC26;I9?ML-*<`0])/=2V`#AW^W;C=[,$J9O)[X6^PYZ +M:?^`^'[B:[GG@/82R+T/_VL; +MZ0Y(VK]/>_?#*643:/]^:T#HLAEU=5C9RY_P)7MA?72M*J3,L;S0)_V+%_K$ +M',N6O8_8>_L"_)^7)JM[@@#70+@,[T:.C`]^*?OZC>,+PV3+ +M4]Q-8CW>^36M_R7)L8J$&73_OZ]1?E^$WQ]WE=_C\/LU92W">IS]Q^[S +M[VO_HUC*^C^:H6\94O\BJC[^[5Q,^4 +M\(G^-C33DW\9"5^BKK&I\3;E7C4_Q,3-/'I2GP1ZGBK)MZJ +MVE;Q?6XEG5W;7.-C3Z9'87X95/+2&LDT3;Q?BN-XZN=V8&PN]O"(O6R_NH"^[TO;S8_979P?8R\&Y'Z7M?VTQX'VF?:R%S^F +M.-&WT-[3"MI[^J+8`WYC>;7H+\4W@<`K_')ES0`P=`YBT(GM:[M;`^]JM4:<.^E3OH?K$_:L-> +MG*G$SQET_\JBB<^,[S.N:.'6%;ZSIW@@>(H/;?\$="]A9!QB/^LL#K$]P+U[D;I?XE?1[]]``Y57%:9Y\%]]2+32W<`8NQ?SMZA/ +M?9[N0K>#;Q]^-\F`-,3'XTFVWY@#S;]>.61^8Z7''!2V?'^J&&^2WTJ^:D^(]FZF9UA+/.N3:OWJ.,WQ4^AZ%:#'B=2O&UB%E_[7KF<:]=B,29,["P.,F_Y1\Q50G6T?R'U8;37"/"V$VS_ZK0A9D8P>9:1 +M_[C_<=J'#;[;::^UI^0.BOM%U>=4%VO]<@_W_DIZAV[]A:>LV_'*F:%$.>[8 +MK_:)5&<3@+LRGN[:+V?I&"LDX)EZG+TB]HW)>>67DQ%W!:]9E5D0HG']JLQC +M[.6%M%>(6U=E\IK5OJL:F#UL7>WO"MSF*5A'\TY?0,]6^UJ_IOW[KW3PY%69 +MS@K2F5NRH0/9!;[IC>+ +M\F@OB:FK_#8/MZ[V")TJI;V0G\->],KR-J]ND?R_<@'QP*TEY(]>X6T0ZY\^ +MVH\3/E7'/"6"OF2>7$+?,-+O%YGV@>22+"F7 +M5^ZS+V-FHD_*]I6.JK_B';I.\U2R_)>S6_N(!]&&+CF!]X)0%NKRY6PZDQPX +MG@0.$Y[K)/\JG,0%N3@(GU(&VN`K5ROQA1)>Q%^FB<\G?,#?-Y"R/HO2\;N: +M]D<3;MHG`#WR4]EQ['HZK:W'^Z8D.D_[:GK,/*V9=XO]T'(>N%_,7\*.`WY4 +MH]PWZ4>>;'4]FO;N*C"]RIWFIF/LU=F*7W6'6H[`"7S(NTDMSWDE^0PM%],W +M0+5*?R%]KE??5N=1:ZIB)'VL:6_>%3O)F?X@=H +M3R8OXP0GQ)C=6C83]B_;=9*EN.@[+]@.&O/Q9,1O+LLF +M'+2WKNHT&^\2_LN'F-S/^IKP"8^QU[)HC0(!.OS:38I.YOG8:]`'&%#\ +MIB>]0P=M@%D(6([G)!56DUZM>\_2O=N5[];M@^[7=JAK&JA7']$.WG)X2D7Y +M(,D68UAOL(^^\\D!K`?MHD?[C0'R>$3YFU<9KH?2^@?Y-;3?[#C[]84=Z/FV +MT=RFM@-SHJTE0!Z?D]YV]?73OM8T[LX4=S2(/CP08L:O;"S@3MB_ +M+?#<,N]D"@L_DO\M;OY>.6R)]>4SOD2:$W0]RVCM=2SJLE?<#Q'D_7O/T/T0 +M']%=TN83[-<.2R6SU!YG-EI;;@U.I?L2K`UCF&67O*\AT("VL":##PVZ_[/% +MQWZ>+?:7(7XK[:U>Q"PH/Y&^3LC&R(Q/N?[823\OC8?_J1+PUTIN)W#=%*?*+?@O_TGUM;]QE_ +M?\RM*PN#;K'>40Q]^P+RO,@[Q)@7.#OWG&&=&0.L"[]I+Y1GV?^R7CLS['_# +M\+U`GY]PH:WTN5:S#"]ZO"YXQ%YXOX2C,]S'.LO)-WVIN&O/1ZPKXW]$.=2/ +M>DH&1L"]DM;_;0KN?N!.CX_[^9;SPWT^=#_?=#ZX:>["FT'?#/B8:QR[`.-_ +M/^TEE_MQ7A]'_J.8J]Z\=DCL-?^2L>@>\\\9K4<-NE^W1/W+R!R=S<=>=\B] +M%^KJX]2!Y+)-"BZ[?IU(O:E]<7FI>J][J?UJ9PJ[ +M?_'#BY>6+2ZRSUU;LO+Z54N6V&T81QS%&$;X7HB_!3!M&\?2=Q[_-1GM)2/Z +MC<\1,6Y_`O"UGS`+^NH+3[`_7T9G(Z%M]J#=/'8,,%6?T?K6?RV/7=\ZB+Q_ +M],M`>/X8E(%^:\.?;3+HX__9H);_#^/H/T?X!N4?R?E_R_\_A2,P4B`=@K]Q +M=/LI9H7^'-AZ!CIY4GQ?T5NUEDW`^,%,?6YI.>]UG:(^_,C;T+^#\'F.PC=( +MW19FMB[HRNQ^'J3Y,=JSZ<5[1]D0>]__=UH?]G3!2^P\*G2M+;60I:.?.,3= +M:^=R\]J9B#OR:C@K\=6*[D2RW\?8P9.*SLZ6WW0<$?X8Z#P"7Q9]QI'7T,;1 +M=Q_9H(RQZ?=TQ6\Z2/<1Q^U%71M +MVC.65Z,=-8.??>!]_Z#[-T7H7\1W7&@K'M#6C'YKZ^R'V!7;`+\=\!W%'OKF +MJTG2^/O'P4L+W<,D_>P/OE#HZ)`^S0='J/\B&JB/QKL8S^P>RVM4&E%FCX\= +M$/W>-L1+O!]L"+M?7TZR@4S,Q]@'^317@7(.R7'6?U]`W\F"YD,HZQ#P']R& +M>MLHY];Y,?9&EOPV];^GQK;E-[(0H!-OY"/`:K_A1("%?P-^XQOH%=\XB`"? +MY`WXH6_X$#!J>`.4OH%>\$V@>-.*`,OXI@-A&D(>`O"\"3_U3?AV;R+_F^T( +MR/LF\I![XT&;\,#R>^P(R.>9@I"-`.OC0?F>30A-"(<0>A!0)DW5'T:>P\AS +M.!-A*@)H/XQ\A^5N2A70ZT6Z45 +M/+8B7RORM8+/UB,(D$U;*@)H;`-=;8!K`TUMP-L&&;:!GS:"`5TT+'@;M+T- +M+^YMT/8VX-\&[K>W(@#^;>"C7?_O0%[O`-\[H/L=T/`.8-X!WG=0_CLH_QV4 +M_P[*?P?P[P#W[U#^[P#_.^#['>!^A_)_!WR_0_F_0_F_(QC@1;6P=I3?#OSM +MD$\[\K2#SW:4TPZYM`-_.SS-=LBS'?C;,19N1QGMD$T[ZJ0=>-J!YUW@>!?Y +MWP4/[R+ON\C[+NAZMQD!A;R+>G@7<.]!3]X#K^^AG/<`^Q[*>@_U]QYX>@\\ +MO8<\[Z'>WT-9[X'.]Y#G?O4^6LC[H.M]P+X/OMX'?>^CG/=1 +MW^\CS_N@J0/T=*#..I"G`[1T5$J/N>,``G2J`W*@+Z`ZP$<'^.A`'CIJKA-R +MZ`0?G:"O$WD[R4I!?IW0QT[D[]R/`!R=**L3?'4"3R?P=`)'%_+33I$NY.U" +MWBZ4VP4:NT!;%\KLZJ%9,@3HE!?ED.?C15E>T.F%++R0FQ=YO."-=IQY(0,O +M\I"WV8WZ[`9<-_S<;K35;M#5#=AN:JMH<]V@L1ME=2-?-\KK1MYNT-8-NFA; +MXP?(_P'*^@#M[`/`?P#Z/D!Y;TQ]^@2C/7%T+QHJ]<]BSR+=6^>#7>!_D_?H +M*;]-T=_R_CPE7MZ7/H$/$;R(>U3$^94XT_`X1P2.G@-_DW?EJ?F5=[^:E][Q +M+%*>>*?S(M_(49[YRC.+>*GJC]!N$7?Z]0N\%J4\>9]?;)QI>)QR+WPLG.1S +MN7*GY*/%EO#?)/VJG.*DF>*G.9BV#,&WAF:B5Q/OU\1KY/+F-)$>@X?D\6:Z +M\G0H3ZLB'[4^$Y6R2,Z).OEHXTS#XR+RT<*I\O$K?"8:R,*(U\WFI0 +MY*/!0_)XJU1YUBC/8M7^$"T:NY$4L1O]LOVK=B[\-^5N^=@T4_PTAS;?D"Z? +M64US*39$W!CE;P`3(_\(OP;RC^'90/XQ?,?*_^VYJEV(XM?*_^T)\OWMJ1_FOSC2!_1SS]-X(90?X.(_V/\!M?_HYX^A_# +M=ZS\WSD2*W^'3O_?4?3_'47_WQ'Z3^FAO\G[U^FIM9^:=U/LN_3G-.F1^Y`I +M3I5MQ+X/CS<9QSN8BE/U\93T&#]/B=/8W/9,>D;S$G_M-N5I5YZJOD7\'L&W +M@?^DQ`_SGX;'.[3P0QIXLU:N>E].Y4=)'^;KJ;S%3]>4JY2IZI(13[JT&+ZT +M:3Z,Q[2^H:H_2II5OK^KRG-(;;^J[=24'>-O:&QP^3IPJOEV>,[V<@ +M3WVZ7IZZ]!AY^K6VT8@G`WE&^(J5YWLU6E\R5I[O*>.0]YS*,SLJ3X=>/V/\ +ML]AR'4;ZJ<0;R=-AI)\QOF)\>3KBZ:-]R^.9*,[9N(-[!O^GB'%M[`OCF2 +M1K9OCJ21[5N\=$VYP^S;<)YT:7'L&\FN*\/8OE%:9Y]\[U+DV=DSW+Y%RM;9 +MMTBY.ONFCQ\F3YU]0T]H;\G70^O8`W1GA_N"`C[WFT^9%&0?M"VB. +M_XW;Y;Z_W[31>H!%K:0V@@MF/L=\7T9H_[3?KZA5G^:?1^CXW +M9S)O48!5?<[&TIH_K?-3?.U.?@!E=6_<*7DAFA>8I8UE7PH(/9_ +M_#Y?7:D/9Y$3W$0T#LM?Y]990/F:ZN7ZCK*(!I\K%?RW4,9:U$KF7\ +MS[/*.FLQ9$MG+'MHKP%]%X*T\M9`D&EY'$A9GP]Z]P&?W\81HW\@)D@O*\`!/-^T9)3F< +MI7L,4!;)H_$I9B69M!5FL370M]9RJO?_<:K?^/K8[^=#-JE*_3;)NOV?.GW= +MJC(A>9!<`(/Z_W5`^:Y-D<>1RVC?#0Q@"Z.Z +M)^N8ZLI91ONZY!H<=%5\?Q5=S_K-!7+_>V1]V>-<(.%I70[P-K%&)]?Q:&\1 +MQ5D4_HX"OHW.DJ&S\+YU5.XIV84VV`@\/G:PK:N2L?#/Q/F9[00/.`O_:?ZW +M'@NR9.>M_'^/LP_7<3<;HOH&;OMQ]L<_H=QTY9F!YV$\Q^'Y(IZ7XODDX,,* +M?";>UR'^9N4)&C]TXID'NGK01F\^+OG(0_I"I8XZY#K?AR_064X$!WSE]`T5 +M\#D!-XOP4]IQ]H>3BJQZD`9#K0;A]PG\8>C +MM/\&>&\!SH1!]Q^*U+V0M"?_K/OUQW<)&_>'RLAW,C45,U--62RUD%W<8.9U +M"AZ/6=G#3WOPPL!%9Z"&-]-9@LU]\IO3%_.(%]IGH9R'FP>\1Z)[?OX@OKU, +MR+"-1KQF__,?YA)]RCZ%*(9:K\;2=YEE$=2+O:RS[L(7G= +M&&`EI*/'2'[B7+D/CU*ZH@.HIP]_0?'4#M#.G:3W2EE4-^52[Z5^*W4)O?E0 +MG%6@QBMEWX+X+"V\@@=U_^%4TC]?M)U0W1.>3*H7'7[HR(<.TE\U7J%MN6(W +M>L+RFT[F643[1O_H;QS+Z\E&;G2QU,?JV06R;?S1K]H#V=]\=`MWIP6#UHJL +MSN`48:<:QO(=@^Z>?/6.K:A^_]'?V23.(CU*Z33?8Z`ZS+*?LHVN-*YY,4K$NA;W3S0<FC_]0NKEGVKD_G]Q9C.]ER9DI*TGW0Z:7V]J$-^`_2E?=VZP&7'+HV=H +M2UN(N.JH3OYI)N$`KAK$UT?C)2R=.9`69!?+<^$C9?=*6F0;H?Z'VLC986WD +M]_-%&Q'MXT\!39F'U#T\`P49J8/NCVP^=D`Y#ZGGB.QC*[*0OL/C^F^4U_-V +MT$RZV-.&>J[WHLYA5^ODG3\?Y:AUCCZI"'7R\8ZQO`[RKT/]+\?[NW@6R:?) +M1D]%#^N0MT:]@XWT(Y#\O(_J?'L%?1^5*>J<=()T@.J>]"!MPSU"!\1Y9(@C +MFD@'@`OMOT61\T?*>5$?]6F_-00N>YL])/9?ETYD%[?9`W16\J?>X"Q&>6@O +M-LD5\MD*VU$(O8%L_CPYVI?]EVA#]*W65>G,COI)1WJ.ZM/=F$[?(/S7!-!S +ME/ID_+9"7NUQ]F-.H?U6M,^%]F_1/C+T34>.LT]OH17C;;3'VSV*5Q5SO_;< +M/FQ$.2#MKVS[H/CI-U2-EOT\/?3.>&ZSBIB"C[2/*/XAY9FNTN43,A)G/UTTZ/YX0K2^ +MCPK[+G7B:";XWR/H*6&7T=[6M!#WD(],NHAZZ`:-'Y>N9V:D7P$:MJJ^"?+8 +M$H98.ODD,]:S*U!&M:HSM4)/CI+=/2+D^27M,_UX>4(`>:+E?AG]?>&_$5Q: +M@'R<>#+_^#.5-_H6,@W^KM"A(.]'.1W."F8]P3[Y'#33OK^.M`T)RCGDG[21 +MW$!3.V1DP?MOE+ZD7?IHGXAO"?A7&>1?I'F#TYC']0/*]ZS2=QT=>"R#M69, +M@UWXI,AGVC[U_+\+_*1(UJ4\]X;>Z8P:N4?7#AE\,H7VZ=([?K?0>7+H2]JD +MW_E)CUIWZ#_:D"Z^15/&%FVR/7\2V?]+.`A.?K/W":.SJ9%'[`.6^G[A;8/N +M3R='[>91T0^V]FO;_ZS +M5--6B(_:VF(=D20DDF0" +MDT"8F;-_:^U]#O-@!IF!/&KSQ_G,G'W6V8^UOGNMM1]G+Q)P-UBX;X82&$]\ +MWHUG"+T7BK[#Y]V89UXM"\_B- +MQ,1UR[KR,#&M\:UN7\0XZO9%S#373>FH6R;4[?,9OM6MK]!1M[["2=9MK^]U +MR[D*ZO:2;W4[=-!1MT,'IU*W_%IZJ#.*X#G3H0?8GO_>WV^+PGJ1V?#_\8): +M^@7W;WJOM9?A-P:],/[[1Q;'\1=L;$8WI.[664AH@852(>#J)G9>$YZ!6$1M +MG5DVC-4@IV5;4]XZ,R*G&S)WTY"MZ0V#"O+6&3/X/;T9TIGB4GX1-1/IK-Y= +M=V>2"-19^%NP"\_^F!ET]RGT,7J;I7,_I+R2^V?2B#P2@&>/X'>'1I691.>1 +M&G@:=^ZJ?/TBQ8'3WE^$_/TB^/> +M>=JG>=KW_N' +M#%/GZ:%0!T]Y?A/S]-"=WGEZ*-7!4YZ7;SP]5`>\5(H\57KGZ2'#)'D:P7EZ +M^+<.GAY>X^#IX3F5#IX>B9PZ3P^G.WC*\YN8IX>?]\[3PWL=/.5Y>>'I +M3,\\/7QJ,?3\)@U_#]?@]X;&4:AX[UL*_Z>QGWQ3B +MN>-(P\_!8W0)>`Z'2!=RC!R[`1C< +M'Y+N:64AX6>T'.U@:?R=64#S6Z=W9L-]G=/S2+A_QNGY%7#_^!`_@Z8QV685 +MSYXX&N_?F4Q'X\7QQ7\,%QW-N43L1T\'6/ +MMCG2^\UBNA+2>YS236(Z^+U'+4[I'6+ZK.&B8Z%.Z:U\W'Q,[3@7MW^OF.84 +M_ZB_5DQ+,I&=B\0T-MX1\?`M>);A\+.S4_@9TOW@RV=[^SX1>'"L9BR>8DEV +M`HL7!3@X8(LG]M+L!'C>(F',#L^I_+9@T!E-0LDK^/WT=04#>`Z9A4@Q`C%N +M8.<(.U\_G,4+Q+DDBXU(\0*W#)"HK-MIWV$RP,\#N1[/^AAX`F@-_#S&+Y/P +MK-PZ=J[*EPO&SMDI>3T)Q@T60SY^MSZP%&F,`VP.,`C>>1_/'3H=G)]UNG1/ +M([RW3GK/$OQZ$MSGF>3WAN(]_(>QYZY&?G:/(UZ;;]CZLIKU*9JZBV/T2W;V +M:T.1%*/N2QC_SF?C7^"E!NYMCEAS#W6S\_:>?JQ[N&@@TG'.I8S%VAEBXYN! +M&(D>OQ..D$FQ#`>">/X#,/ZYKI!CX,LT;#>DI8\_XVG@&9$^SS%/P^E1EB:1 +MUU[/5-41/9Y#A?%9V!E/)1BG((*^I[(0'N_F^)W/7FI\%<[P$SSOGYT\?7R?U +M$;%OQ$!:H://\?<@K5KZEAR_`6?GZ*JA;=?C]WC'3P&&(W;^DNA7V.2D`7[Q +MG'8?ZRN>I9.=R?OB0!?T18_G6YC(B$HA.1N";12TY4U`L*PLZO*A*:V7ED\#YD87H0BN:=R9]"BU[I$.J5S?DXT@4+19WCN2Y>P7<%] +MM*(3>J#W>`[Q<-')/N!_%MO\*O5=\85E5H/^KW.)1W__#V^;E9,;,^W',O"?=SM53 +M#87DQ0^]%O`.CP=W"OJ_F9W?)."Y=<.I-7S.[=0!/!]8`!^!QPX]U8K8IT%; +MLQJVX5EV,N#7J5_A,\BC&W1]DJC;,3XP/V?,3E3LK`/!PN(''B:6A_%\/#P3 +M0"@ZE8[K(J#;6:PQ&I*=2(-S(W%=&-=OC%7(NT$]Y'4W7`]@_";P]?/@%WR* +MP:2QM;RQ^'"#:\"U4SK.T+-\G^NWP4TFLB,(:6?($J&N@SL<^!RLDO`)_VNE +M>&CPO\DD7UPM_M]G(G,3W,JRB_..%K[6-(CK='?#]0#0]TDQ4Y^6LW,$#@>H +M\(R60=M8+%7&HX`(GI=E`0U^I1ECQ0X7#8'_8U8PFNSCC>6CRB5&_+)P- +MPL[!J-BJ3$'?$,_[86O2640.MC1P118_OQ;CFM$MFE#I7$II8G8AS6SZ[))9*U`(L.SB)*=!^1T)F7%&<>9E'BV#RUJ +MB,8S*>VE32T8\QG^:^@9^PC\LI@GXIGG"I?S*3=H\"P\'#MHT'[@^92^Z7,+ +MX;*T)$KQ6YW2P/\WM_*T0?&L,DNFB10-C-FTD*TY.!XPD5,V:LWA%FB++:S2 +MTQV.6)U\[<>91X2@&9L!-BH0KIEX)8WIS6S:C>`KK`KV+F*-)L>Q',>X;TNC%$&&"Y)MN67)-NI;86=!,+O$3P3 +M)=E^A+(X+Z,\;J;]#&W',QF6`W[K\7S&M2QNQV[Y3D +M0S[OK[#/5/-S&L[LG2B^&.JW)^<0.7XW;M\]I,[9Y@8>@GQVKH;)`0K?/A/\GFVC@0!*5S=#`N!FO&!IX7$V+CX]` +M6@S=/D-/`X&F^$07O`/V=\8"^%U`BT%'!IY*!9IX>`>O!+AOH\5#)DA+@'N; +M>MLK8Y1">`RNDP86G"8S#Y-1MA9O!`\9U[?$\EP +M*>C;>.:?A,4LHO++>FG1C$$:IEU$*V(ZJ/SF,EHTZV,:IH-[;0<-W%E(BQZZ +M"VB5M$(']S_-HL55.4`/]S%5-/"Y=%K\LTR@AWLMW+\&[:E.!?I(H(?[7P31 +MXB:XU\)]3#4-?#.3%K_1`O1PKX7[YU-H\5LJH%KC7PGV=F1;7%@)]%-##?6,W+6[(`'JXCZFE@:\8:/'+BX`>[K5P +MW]Q.BU]=`/1JH(?[??#^KWN`'NYC&FG@?RGH:P$M$;8""FDP/@==N6M^5H0M +MG=`*?7P$QN#8-0AY_%Y%`_\[!O)00]Z-M/CM1!KX3BKD$0UY[Z'%[V;1P/=* +MH$RXU\+]?LC[+RU`#_QW<_R\\_ULUT,<`_5Y:_(]N&OA/;!/<:^'>E$X#>_<`/=SK]M(0&T$<8(P9 +M:HV61!S/'\+G[T?@N.[J8^RYT=(O +M8BD`GQU"6A,9#>)Z=;B&%@60H:#X2$)HG*H" +M=!+NZ8LP$[DZ/[JEEUAQKYJ%ALY/Y+IJ]"5;6>X2C*<.S[87C-#3?,S,TJOX +M?B_V/P_C8(O_,_DXVEJ([S+_Y31A;;,7JUMI^/P4Z!-W0-ZOOBV8Y>%F,D.= +M_0[D/SK`Q@TB+0U4MPJ,ECW[Z)U\D[R6Q7,8M8W-,93'J?`L(2@C$6TE/P_! +MFG;`AG$*K26QFX@B8B1(EK^9G5MQVK#U8[8.C_D#W3TT)#<1SZO!T[@S@/:LUTB@_?A7,&Z$OB&4@K['8\L['$^UR(%?3_:(RX;LW. +MI,2YD,/$OI#OH[2V@7W(8>THRPZT416QA<8VVD/C-!@#:+1`1)R)Q=RQ:HAZ,2%YATC$X85$GG^0"LLR<%Q!\)S` +MD(Z^X^0A2!\U@ +M^]F*.(RY$O%!_R_PO.V0W7C6\&L!5>#G@&Z#,@LTB@,#N+NZ%^=E@L(&(NB! +M@3Y,#[=NT2C.;M$$CF[1!)VMB.WH[.DGNDSP:Z"]U@)(*]"$C!9H0FE87%=] +M-@FU[QI2VBOT>GM%;,W95P,:<>[.V#-"M)DLSG1D73[TD9K!YLMZ2*3UU0"V +MO^R>&^A)&ABV`/@XCQ8'MM!B>:+Z_Z#MM4?B6(H&!ENH3!$%%_B`C->$!LJ" +M1)IX2%?1XH!"H(V"YU'PZW&]=[C(G@'R8?XN%:#_5L1XM#_L+&`LHS)&7R6, +MG:$,.M]>`9AD\3YHN3X^_Q0)P&?-.-\.]^J-,FJTC!!X1T%5@^W-9\QR:&M[ +M)Z3!NZ]6G1'SK9F?5?`$8;%!,`X28A7&**%P*=0;41_8/\=Q"]+14'V\`'G0 +MBMAF&J97"Q6Q+:!'"^F<(;6P:W[J<)$0#VU.D<:1G4V@OX(>;Z9WV(9;7<@+?"L"LH*L%LS$^"Z#:[;[15Q*7`UVTNM^^!J@:L5+@-<;7"U +MTU)K!_QVP740KFZX>N`RP=5GWS4_'<;\*KC4<$5!'0S2'!H]0[/$<_4V54#= +M05=%05I.N<#/W2I81_N,.29US\P?^C+5A_S,WB=0Z@!?:8:PK/0 +M"G6@WSM-`R390@HZ^D9)00:UL_D0G$.$,1SV(2M-55M#8VN$4'T_GEEN!]VX +M#6,.6,4QGY6/^3JS^DG8=O(2CT'P"?E!*BT$W1INM6IFUV]EL0>"A:"7\^+GU`3I;]/D5"Z^0"GB?X.:V,8^=%Q^;@NH:)U(-M[SQC(8=E +M@3.LS)[,CQ5H)D']M/P&HK#F9MZ>E4VNJP(]:@V+2SLF4X3>DR50N`KP/&(A +M+"Z5BK*TEL?6X-ZK`__\$^2G.-II@K+7C9>E#60IX1]]`Y37F/Q`EBC#;>NH +M$.Z$?^:;@0QM(O[K05Y"+NCS4=#GHMQ@V*FLP_@:QT%N\N?-=;@?M+2II!W+:`W+(!BW*B`3XT@T\BMX)O4"7%P`4>V\'VV.0D,;R?*&O7$SV> +M2\C\H)-0]GH2T[">1->?`%]#C*6[_+22Q=JE@:0%GK$SE`7I#.4A#8Z'Q^R) +M[:1&+I5%2S!&(\9V_`!X-^,CH2P_U5Z2OX0^I@JI74OTE6M)#&`LNN$8E+4% +MX\A!.\5XP01#8OMHN#O@0P2ZX!/=`/PYP2)@?(C83P>7;\>ZET`/'*SNW;@ +M4]UZL>ZE8MU!+@U.;X#M9OP$[CGLAP9?_MF"% +M_E,F]I^"S-NEOG-8-O/[D^L[0?.^9GTG$OL.]AO6?\YIWPD.N[C[3G"HA[YS +MT$O?47GI.ZISUW>"]YX_FQ.Z?;1HO,T9M3ILSF@%VIR00D_]QEH#_294[#=_ +M_P/D=]FJSK0)^DVYOM^FXKX#!=\!?52/?4?F\!T0[U+?J>5]1P5]2(]^0^56 +ME_X3.:[_!//^\^(HB7ZQR,UG`#S7#SKD-`I]`'V$"V][0N^]N/M/Z**+V_:$ +M#IP_VQ.Q?[3(N^T9I=SV8!\Z+`O;/[D^%/[ZU[0/G4<;%/'3B[L/151=W#9( +MZ77_AWHN*>R5*4N-M6Q/3*'1]AW"UNEERE\95?%$*,MMPC@WN/[_G--Z/%LG +MDRD_DO8'-(BQ<";:'T!+^QS)MDL)2W-76`.SEW@;;X[ +M?#NEN'8^-#<@<:B,D"J<2RZ>E01M9&O80R%O%`X5I1!(2Y/26!SDH@`JP#/H +MR^MP[1S/0->=(;)CLEGKH*]&XMHU6QN]ZAH+]`%%YPZV+P7W(316(D8#;JRA +M<@41=@VI:%A<.J[#50&>I+T)5%Y`A3F#+;AVQL[IA?K],=\LQ]C:]1N)LAYC +MGE;$I4.]+-*\E;[4RZ4V"$=X\OA'N`(X`6NBX)N +MDPOU`8D8>_676_OEN$9N__DU%MR/A?L+,(;#8=GL4\E'"8_;)YMMQ[GK>^$> +MUQV%(96"[=V2S7X=Y\TCLDA0);1-I,U!6I-L-EO_A?MRG(O%6*XX1T=KAE2L +M#Y?'I0MSAE0LQ@VTT&6A",<8=^\TGRFI(PWS@-]2P?A3SSL2\O<8/"XU+ +MIX!M'F]#]E&O[/+:"!FEM&:P!;^+Q7E:=NZ[[/+-D+\">3%JQ[XSX>X"ERYL`FRRV%MY[ +MW1LGRNV`"K#A+CL)(X`+"2-8W[?R37)F%V21-[$]%"!#^XLJ!9L3M:)<>O'9 +M<93+SJT@$TZK=-2?W3-Y"B`+MO]"%ODNBX=U!N1X);7@"<84<0L8^>7&$3F. +M,S#]CX,C\IW'",8X(A5KB:(N&W@.>5+58$NL),>M(+MLP"[P?"?P#VP-SN6W +MU`LD5``ZDRS2!ODIZZZD^.W<`/"P=;@XLEOB(=8)]_I5P'.@7<(Q%!DMM=D; +M/RM^2:(J="0>YY1HP#4L)NMB\`1P?);\S&?4:+83G!O"N6_[TS?.3GX*^`5V +MMV"$*(WY9CPCPFPDIXC]2=#SH/O!9BH*3I$YR"\A++;FL.R*-Y=N(H6^Z=$K +MV/>L:.=!S\O97GPS^`D5L34^YN-U_A/;+92\W"*(;4[&-EM&",[A+GX"SV$_ +M3832EUNPS>!S)/H^EWMEPD1E4YS#>_J:1B$T+A'+QCI@;);%IPGC._+?.&(G +M--BY#C9?Z^"]_3H25?Y++G?[TR(/,G'M)2ZQ,\M,%F,=S,?)O<<)D[]1,T#L +M`3?.QG5ZE'^^#>1_9@2_L30;"\\2]#%L0QKY>/E?]>;2'%_E?]4TR?^JBZ#] +M*C_:KYJF]JLN@O;/\:/]PG"8Y1H`[B]NOI5]-'QK!3I +MGMF441+D=A_J=J]TNX]TNU>YW4=)]V!'9IR077T%^*&%)MG5S>+S:/SV%VQZ +M]`3Q0J\Y)KN&Q_-[^FH-W5I(A!+KP<7;0'>!_*A5%0@^PE7X[3S&FU)O?(?V +MRJX)QO;IL@Q$"+A1@[Z@4&H]:`ZX\6JZ:WYZI5P(JBP2U-O,U+SH%.BATQB3 +M`F2\B9H-&X]#?:]YXD`>Y`WE0%E=X(?O,]JZP(^W'NRTM!-C3C<1\^RBP=9] +MF*='_[0\9B&4^9VE&@6AVQ61OLGTFFJO?F]Y3!O/-\B??+V.?VFY5JROTH]\ +MHQ9.D*]87[4_^=9ZSU!N4D38<`_^<^=4/[^R7ZN>2+9^R?WZ[SBG\5:+X_!^9\4HD8 +MS<0"?B?+)\1&*O,3B1AST(+GM22?PB@1(SCNGP7OV0"_D,<(>4LPRQ(W,CHS +MBV=;&2.G!1JR`_X?EJGO##`3>3WXT\/%Z@233+U//(=L@)T]`O5]&OQZI,4S +M2'191([GEP%M)IY'XFW,.50>I\!WAT+C2$$=87M3*@429`F-"[KG!CH,[S=- +M]#[H^=G)*FKFWQ$.`F^N#T,>T'*]V5*@FEH*/;V!BQU-K&8H.! +M+EZJD?OH/WZ#[:\US$5;]8W3D/]W>V7?2.5S"M9N]EV-3&V!,KKAO@3IX'F. +M^-SD]-R$>\_$YS7B\SZGYWU9I_=[WLO1D[OK4`;?&##)%_-SW63J$=;V(&N+%U1+YB@,AA'!J&^WZ<]\=ORZ1"3>KSV;X)Y:*5,&8=S@P&K`C_F08ZHVQ/\UOY +M73-L.([9H@JANTEX13:)P3WT*SXI9'-T+\J%U-%AC=JV13,7\KU>VD/T\'$E +MJ<,YO.*K@G#_$/I\N%<`U\B03W2+)@K/[XXULWW[.*^KK%]+@CKZ!M'&:VPP +M-L*YQ/_L(7+<5__B&1)/0^-2%AU2D_V:`^`+_)H4G*6?2]]1"Q5Z,PV+2_$= +M@]_W?1;X#4*XA>^] +MPN_+<(Y7W`,XVSAB(G6XUT+\+D'(S0P6@,^T2$[>/#,RPP[^\_`DE8=\Q%%^Y`]Y1X+XB6RGP +MVQ)!0;8RX8B&V*V:J%B^YHDQHY7UATG0@-6K2V?0) +MB%W +MT->4W_,2IL;O_SCH=9U+2)1WU'23MZ[N!ILZ/QYXW2P\JE*P;^!D-T8?@-&6 +M8>,Q]I_9,-60&F/'87Q?Y$/6>G+=,=D-'R7;3E*Z:TA=A6=NHNQVC^`>5V83 +M#N>`W$:(W#YGL$D(BS/87PU(]&0;<*XY+(9F1PC +M^HF2@DU(NP%C&W,Y_G(KR!%L0OD9+D?#SDMX:=XW-TM +M1PPYYIG_CL]>-63W.LTSW\C/RI3=V#541.1P7\[3-8N?!C5OXD/$^-U_'?)IGK2\3>:IZ;CM2V7;*HO_(ZIFAJ_ +M8[V>_S.Q38W+<=C4N!SO-C5NWB6;.AF;JB-3LZEZLZM-G3]OZC8U;J&K39VO +MY'G'ON2[38T[Y=VFQIUUM:EQ'=QVSH_F-C7.Q--C&YW3'3:5IW.;.E\_>9L: +M<8%LJM*+357^F]C4;WN=_[RDXSWI^/D+IZ;C;PKU3\??DN;0\;>D>=?QMP1? +MTO&3T?'?[IN:CK_YH*N.CP^>NHZ_1>.JXV_A9S[(;GK&=QU_RR?>=?PMG[KJ +M^%N:N2Z/#^(Z_I8VGG[3#N=TAX[GZ5S'QT=.7L?/O$`Z/LB+C@_Z-]'QMWK= +MXSBQSOENET/G?+?+N\[Y[K9+.F,S6=\]TD5YV3L&WJ.N>[M:XZ)R&3 +MYWWK6=]U3L+WO>N21IF(AB? +MS*&S!KC.LL\94J/>JH+W)-WUIJB[$.^(>\2\1YTU0(7%ZP*H'7048I[KKD,$ +ML8^81YV%6'_1">M<3W4[]-0?0$^=$?64B''\1@OQ;0$]97/64V/XOD(YIJ=* +MW?34!C<]M7Z\GOJFB>NIY\;IJ3]-DYZZ7>.*[SL>EO!M\QO?M^>YXON.)+'O +M'/`=WW?<,(;OQ]SQ?4><*[[O4'`9MZ +M:3WUPMC41*_K'Y?64SV-F^[(F]JXZ2ZO^U\NS?UZXO>=45/C]]W[+LT+^,+O +MN\JGQN][HOWST;]7[O#1OU?.??3YJ>-]]._=NAA]])(\'G=/=L]'((<`X\@( +MP70\I^>2[SZ1[WZW;6J^^_<4KKY-TJU3]]V_E^KJVR1%B[*M\]VW29KAW7=/ +M"G/U;;YGXCY,4CSW;;YGX>GWU#JG.WP;GLY]FZ2%E]99+W;?YOO-EVRM+[H_ +M*75JNO\'ZDNVUA=^?W]D:OQ>5.6?K5T\XK"UBT>\V]K%K[K:VGMONV1K?;&U +M/\BU]T5=TOV^Z/[DMJGI_OO+_=/]2RP. +MW;_$XEWW+WG)5??_\-9+NM\7W7]?QM1T_Y)UKKI_Z4M3U_U+#*ZZ?VFY*-L9 +MONO^I8][U_U+-[OJ_J5)7,2Z?VD:3_^APCG=H?MY.M?]2_=>6I>]V'7_ +M`RK_=-&R0HKM+=/$+PF>=UW/FIE]9Q +MIWL=]R&W_:\/SYOZ.FZ:V_[7A\7]KP]N\QWW:8[]K^/6<=/<]K^FB>NU#XO[ +M7]/$_:\/YCFG.W#/TSGN'_[*_:_VT+@VM*DZ7,^I@==FE?VA=_+35/C +M]\J<2W,+OO`[/65J_'ZDWS]_?E66PY]?Q>8NA9KYJ:A/%X/?`OI74;Z1*,!/ +M(,^"CX[G'1K[]I.L?/1[5EV;G./L]_SX=>;SX!ELZ`^!?X,Q7>P5<08;^/'H +MYXSY^^M=_7W;O("%UK"XMJ%=@TV^^OMUHJ]3^R_M[Z^_J]ZR^5O)[ +MABK\]7M6);CZ/:N#1#FO]-WO6774N[^_ZI2KW[.JC?LWJZ/L&#]*MJJ;I_\X +MW3G=X??P=$B3O=>/YX>LCIF$[]/NA^^3]*_A^\RYR'V?1[V>_R7:AC8WV[#P +MW]LVK$Z_QQ.\UBZ;&[\=,$_D]WN=VUI=\..;WK"]Q]WO0S[EW!/R?;*+`^9ZR]>`' +M@0]D-+U-*L`/*L/XK^O`%[*]*OI"ZV]R]876_=:3+P1^3J(-_"'TA>S@[UA? +M#5A8=<+)+UKMZA>-5L2UC\X+2++.\=TODN:`\-S[?UV_:&W+U/PBZ&0N?M&& +MFR2_R.KW?-#Z):Y^T0:U*/-EOOM%&V3>YX,V!+OZ1>M[N/^S80&?#UIOYNGK +M4IW3'7X13^?S01L2)[$FTR:`WK]P>]^\KF#XU.[RYQ-4.9[\]]?F)S3VN=CB[292SS'<[G%WJ?7XB^Z>N=C@[@]O; +M[%8^/Y&=P],W$>=TAQWFZ8[YB>R.R:_-1%Q$:S/*?Y.UF9P%EVR#+[8A>XIK +M!4_M\<\VY,4X;$->C&^V(?=OKK9AZZI+ML%?VY"3-S7;D+O'U3;D_6WJMB%W +MQ-4VY+6)OC7*.=UA&WBZPS;D +M]5_:.W>Q[YW+7^*?OBIL=>BKPE;O>^<*U[CNG2N07=H[YXL^RIOB]Y*%"USU +MT?8U4]\[5UCBJH^V+^%YY_MQ%L3VF[SKH^VWN>JC[4JN=[9G\CFC[6(\L?P. +MYW2'/LIW.@MB^Z;)KZ/YY*>>XW6TZ?)3+_9UM**>2_-%D_51MY=,S4?=X?7\ +MWXG7&8K['>L,Q?W>UAG*LMF>4D7Y>J(HPW6&K@-LG:%T;)U!\EN+GW?U6Y^Y +MUI/?*JTOV-S7%WXTSG=-.EL1U^[/^L)NT7?=_2^]OO!T]-1L17&ZJZWXR?-3 +M7U\HWN=J*WY2R//>\:[OMN(G*[VO+_QDC:NM^$D"MPD_J>:VXB)?=9G2QP^Z[/CUDEQ?;1BD*^- +MCHVQ39^*NNI9MS71TO>]Z2IOZY\XK@8_=J$_8^NOQ_KG3_9-33\]Z[;^67[3 +MU,?6S[JM?Y:+ZY^EVWW73^4R[[YLN=OZY[/B^F?Y`CZV?E9<_RPM=$YWZ">> +M[AA;ET]X_@*^L_"7, +M`^IJAO:D>*TKY)7\^ERRS4:/&-8/DCY9Q:T1`S.3:$$J0;^I^6JS`O/$V-'P +M+`J?V:&.^8\3>>7@S"#HZS,*++1_@PWJ##A[9VZWXJW=D&8GJDZ+G>4!/B/4 +MI^*V>J#W5`>,-SKC!FKR#3L57L\_B-@^EQAN!-ZIB=S30[A9E!&&.N@<5&K\PPR:YFL=LP-AS&E`/;!KBIS'LW9WQ,MXT9CZ]4 +M;UKS^*H-FS>IYV6$8$=S*D.)Y=#`RI+G9A-%PVQ"L$S(J]TD7Y7!8F[#O1QH +M"LY"/?)G!C5@#+O`BI$&^`_E8WTL4GTD>1>`O'6@4WCLN)US(G)F?H_FIA)X +MUR*46ON'BW=J)-S0,FOS,-IFFGD[8F&I1D9XO]BY9$A.[@#:)2;RI&6LO2]( +M[=VY[MT\[S'L(EX7R'YX3D-C],EV7B?$^%!!)HG=BC&KJZ*%RABE4!Z31,MC +M%D#=VRNSB7J%74DJ0+^:*V+T8;)"65@FD8,.U4/?THO]HAWK^)=_/N?XIU-#?`NZG/?WJ_2>\6GH]W-GMO]TRAH=Q6TNQW:O<]# +MNYMYNV<,3G^[J_C^W^*J1O_:_5.O\1\<[=9ZD??/(H5*+I"W5I3W +M9;W3W^Z?FGB[?UKC7[M_IIY$N[W(^[E0:#?(6PORUGJ0MU:4]ZR/I[_=/^OF +M[?Y9E7_M?L[K_@='NW5>Y%VM$"IU(&\=R%OG0=XZ4=XWETU_NY_C9T$7/U?B +M7[NKE9-HMQ=Y5]N@W2!O'.E'>#]TU_>VN;N?MKB[TK]W/>_7_G?1: +M/.BN<3(WYL03W1EL_POK:%A,_&7;"V4PCDNX#,8L+Q8)F1$F$@QMS:25^G3\ +M%2`?&^A^>VC,@M$2:_NSJXFZP$R4G?DF`N,T2_%L53E8:('_[&0@OVH$H@:\M7K"VKN'\>D&$VC:!^5./8`?O5T9L$8K?CY0EH1DRB`S?(Q7Z_? +M/SGQNP7X/0YK#G[_O`?XW2+RN_6RGAF#7OC=`OQN!W[OFSR_?[[?C=]5YX?? +M/Z\2^=WERN^?+^+\?L$,_#8`OYM]S-?K]T].>ASP/=Z&.?B]:Q$-TXKXU@*^ +M+^OUS&\MX%L+^-;Z@.]=-[GR6WN>\+U+'/]IW?!=T\KY79-.*[2`;ZV/^-Z5 +M.@E^`[['VTX'OW>W`K]%?&L!W[,^]L)OP+<6\*WU`=^[7W7C]WG"]V[Q_"^M +M&[YWQW!^[^H"?@.^M3[B>W?[).P6X'N\S7;PNS:&ANE$?.L`WS>7>>:W#O"M +M`WSK?,!W[16N_-:=)WR_R/V_<>/W><)W78K(;S=\UX5R?M67AA_ +MY16U9W_E97'\\W*A?_[**Y,8_XSW5\;S?(_IW/DL>]Z_,#[+GFK//LN>%,[S +M5RS^^2Q[!OSQ6<;S_+4EY\YO>>VV"^.WO!;DV6]YM8WS_-5,__R6U]+]\5N0 +MU\AS]%4XSYL4-#Q&&0$\C[`1.>-S$2EQYC?R"_T5MJ95J=\(MC2!;E`16[A^ +MJ_"%BE@K]5G"BE22?/0S:K2U$Z/B#(FUD?#.'2=(\E-0CY,:\H/-!.0\0A9G +MD0CH4\J&(%\\LVD(`?R(:<(6P,(?B,+ +MY_QW9A/]SC-$_9<10I(IEV/#&5_E^/_$^;_7>NI!C+C/R+?WFR8Q_S?>YW&2 +M5PN7U^O[0%Y57%XS!B>0USXG>;6>1WFU^"FO=E%>S=,CK]<+N;R:ROV3U^L& +M?_PEA[RT8O]Z(X.&:\7^=5FO=WEIG?J7]CSV+ZV?_4LK]B_M-/6O-_1<7K]( +M\$]>;W@]_V`B7\M)7F+_>C,2Y"7VKUD?3R`OI_ZE/8_]2^MG_]**_4L[3?UK +MK[C^]<:`?_)Z\=&+_^F4;#=>)_>OF,N_RTCGU+]UY[%\Z/_N7 +M3NQ?NFGJ7[_D\]_%;];Z)Z]?>MV',I&/YR0OL7\U;P)YB?WKH;LFD)=3_]*= +MQ_ZE\[-_Z<3^I9NF_M6`G>#:8_)3GU%U-IF!>RMZ9;^>DW:6[5/3UX,/+02]D;7S +M$:)`'QO]:N-@"T'>>Y&%LA9IMG:3/N!;9U8WR&8KL:/L@;_V?/&B$884*]%7W]7\]B^P71!\3]((]I",AL$_K[TR.;7XG[ +MP7[5+,@!9X6#Q&C[.PG/(PGX3S)^[N^:T<_EF-"ZZ0FM!SUQ66^O['=W7EA,_.Y6C@GP97$? +MT;1CXG=BS(+?=GO&Q&^7N8T+I@D3OTWEF/AMM'^8^%WK]/C2+6WH2XN8<-,3 +M6@]Z8M;'O;*6I1<6$RWWBIAHQ[G0Z<=$BXICXK\&/&/BOQYW&WM,$R;^:QW' +MQ'_%^X>)ELGX?Y/PU__0C?XZQX3.34_H/.B)F\MZ97]85'!/@D^/^ +MLVG'Q!]B.";^0#QCXO?;W<8WTX2)WQ=R3/S>3Q_S#WW3,R9H'<`Q@8@)-SVA +M\Z`G'KJK5]:Z[<)BHG6SB(EVG$N>?DRT)G),M$9ZQL1_/^\VAIHF3/QW-+=_R:_W;#A:@OWHWR??QA>//"XL+PZKD=?QBR."X,"SWC +MXIW]YV;\\4X;Q\4[5?[AXMW)[/_WN"]A_!CDO06^CT'^M/_"XN)/;Y_;,2_1W[\3X<<;%>Z?/S3CD/0O'Q7L&_W"Q?Q+[?SWO[Q@_%OE+AN]CD3^? +MOK"X^//Q:8_FN/B+V3]^]!3[)P&A@D;8$+"@STT +MKGL4Y/N#3S^C(-\DVU]59$,_I<:<%M*1/D)BM(<$=? +M/SQK(7:C)K0CW40Z^KI(0091=HRVD[P^:NXH?)\D'P(\Y",>/KR'\3O(VF[[ +MJX;@/B;IFS!C;Q/#8D$>8''4#8M&C?)%W`\5RLW^CIN>7$B_-@D_'P0_V%J"^#G`X5_^/EP$M\_ +M3&;-Y(!-&K-8G<8L(GY:QN,'QRP'*BX&G(X?CIV.A?_CIG,3WCY-97^E*D,8V5J>Q#<>/UH/^P;&-\<"% +MPX]Q_]3PP\=#TX-!_^`8Z..P"X>?CV=,$3_MTE[VZ<'/1Z+_\]$FS_CI.NOK +M>&ER^.D:X?CI:O,//Q]/PO^9S+K-P59IK&1U&BMQ_.@\Z!\<*QV\@/[/P2GZ +M/WQ\-7WX.2CZ/W_UXO_\]59?QU63P\]?1?_GKW[Z/P]`^.J?[G`OH__S-%_X>/PZ8//_\C^C^?>/%_/GG*U_'7Y/#SB>C_ +M?.*G__._?IW_X'D]Z&^)OH_!NC^ZA1Q#/7[NE?STX/2M29E"?1^/?59WX3#TV?,7UWCLLW2.H<\4GC'T +MS])S,Q[[9PG'T#^7^(8[/,]'$.? +M)WG&D.G`N1F3F3HXADPU_F&H-VWZUKKZJGT?E_7-NW`8ZKOVXAJ7?='/,?1% +MN6<,?3'KW(S+OE!R#/7Z^3UR7Z,[AN[=O&[3FJQUJ]0+[[U3N^2>'RZYXP[W +MLV6#:'E&C3+P6S_%T6NH[(`*&1<,&_PSO@JH:K$:YVN+K@ZH/+#)>-R(XHX`J%"^B/ +M1,$5#=="N);`M8[G!]F@\ +MT.3Q9T>!]I@Z8OMU)G9.L.SP$R#'`%JZIR;K1G@B.WR;<0>+<]'.SRX_?!O6 +M?;CX<(*);"[!\VQ]D\WA!/']/!.QY^#[)JP_I)F#@9]%1('W0NDK3:-A^G[\ +M?W;78#/(1H5C-T/^RZ17=OB*T;#YB>K[(RC\CX3ZRH%.G(,Z'#D:;"T1:N9G +M0;I"*+468AJ%_(1R?3S\[V;I-8/MTG_X/2A4QJ7B;_AV8F'W18F$_RH)RW/. +M_,SAXB-Z$[&68YW5U^.9OX>O$'C94-\]-5@/6A'7A?=+;R#*B"XB5^='M_#Z +MQJE,LB,)2$\K8QN!IH1"W>Q%1*->3Q)[94?J[*6Y,9!'(BW-U0.]AK6]0@^_ +M1ZK@F=YQ!C=KLPSH$J!.;2:9L@?K-)1-;7!_T$2^V>',5Z?WKN`\.M)B#LY- +ML&;3KIU_)P3J0$?/T(Z\%#I2^2,"^JJ'%*PD49=MI^9D.XFXK">"'DCI(1@# +M(*^+]G=FM9*\0R1TVQH269!"S?D==,28TT,Z+7!E'0"Z3^!JQ7/Y%8NSS!A_ +M(+0SZS2D'2=YZ=1VH`/I]I-DFSEBVW&BJ(0*,CK2=O;5@&:* +M]<.SRZ&MV#:A0J]:84,,'6D'G4"DY_C,'*8'_O;O3;;18:072G/CAXO[31)O +M)%X`S4%H>WQ`%E$,%Q\%V5[>XB1/)?`U4*(5::)-LMG$0<-D.$8#SQ="'D&\ +MC*,*I!'UB$0;0$NR4^QA<6HJIR,@\QHZI`I=;*,C0D5L%>B*4#S#G=,?_12> +M-X(MPO_[L1QAUV`CZI7D+J`OM9HK-X+>J8A3,@R'Z1//[IJ?@GT*<4=+X%TI +MGR(R`^I32^%]6AY;)[ +M$O.I`>ITNG.DW\>S]H\96/M&"XDQ;S[!\^Y/R`;F&O#H;;?Z7OT^N(Q3D7.);&[[<@66"'W#"/&>P!&,`[,R?$71W+34, +M73W8-5S\98]TOKJ/^?9(;:\7VPYI3&?'UK`8#G`_X%5>@+5F?!?:GM@@\BQ[?KQ/;I +M;=<)K^M_3NW2CV_7R;@IM,LVOETGETUONTZF^ZP/*^.RN#XTS_!='Y[L&J\/ +M3]WDT(>GON&7/BRSBG;'?'RC# +M\+BLX6+S@'_ZT#PP7A^:%[GJPU/1WG&H5WO2A\FVDY1C0^_CF.Q4W@28;YFX +MK-@6'\OR>OZS5QR^%I#"<3A8X3L.!U/'XW#H;0<.AU[W"X>5L6(,\Z'-XW!8 +M'MOD&8=#]S$<5L0V^=:&H:@Q'(;%-KG@\%L!*M(`,P[@#XS_9AS6A-"0[D%I5 +M+%ZA&/LIFHKQON!2U8E\A?]ZC.]4F4UBPK8KB13'*6P`Z%BLI^?-&,<)YS"$ +M8!['B3ZI(0T;21".>^J*:+,=8S4%-;4(>P*J("U1^%9`=1V,]Z'\D(I1$E._ +ME>!WWAHZI)%C3,/EIY4$VIXJ0-Z0KH`QH6[S:?YQ[2MXV%\]$.1*1,S,)YZ&&T*^P\!IGA_H\)QNTL.$7"`#L,]P+@ +M/KDG8@3ZKP#CU@C4!WC&CG'`1(23FO#=ZTFD[63F;//)S,MWB;'EZ]8#7C=` +M/SA!8B(&W&-P5I0`)GD?$&.9T2V`TVS`J1QP*J?-M@T:>9V<$J@G\/ZT"F,5 +MX9B-7CVDQS$5C&EM;X$\9MP@5\,X+^6#@_T8[TS&=9KMGM%=(,,?T9.]LN%N +MR#-(_>!_*$&>]_#QKFTAG3/8:)(-[\5X6C@'^N$OXIAB#%.,9?I5<4PA3Q8/ +M#GR.1D<,P&.N,>$P%EP1\FNX^8.#(\0[GD>?=^!Y]'D'GD>9_^&,Z;/`V[/` +MS]%0"=-G7Y]63'=YP/1B+YCN\H[I72><,)U_83#]80_#=(!U%\/J/IQG-LE& +M"R7,?L#F)JVM%QMF1W,U:KBF';.CN9/'[(<]HT3B&V*7EKS_8[0N"[H*XK# +M,ON=\"N'>GP*OP'P^R;6PS>;9F/G'X/=2A-UWU.L_]U(PHZ)_Z$^!QWSRB*] +M@Q]96#;P1,'GNOF]4WY/L'NBB,(X;[C^(L5Z$W]O8W/CP`?TX43]FL/+M2\" +MF]OG7*XG?IAD]DQOSWSCA7V'MWSNT=BICWEY/?^$J@8+DYOL!L&:&K$_[[B/ +M/HC@]?OGY!OF0C\>++2'ZE/0IP*^AJ)?U:FQ$6MN*GG]F$D>/D"4BRWOT)W' +MP/_*4=`#HREL/J>3`TO +M6T_8G+5A^2]`]](;9FP#70.ZD^E@4?^^6#1Y_3OZ](WI]V;1D(*$8?X81YKIY&6@D^%^^:%"YJ-*.IG-G6X"W73:H9/1O^Y,-Q'0-S,79T6, +M8*Q=KI\.X1E_X1BSUSC03]#?L&[@NIGI)PM1[CXAZN;U3KI9](M==+.;SRWI +M9NM)C7QW$>CFOQ'Y+@'T,NCDO8^8Y3;0)S[)%WP-;_)=#/8398KX.=!D(_D6 +M>L2.9S&#?1P%V_CZVFYY>`[(-^L=:GLR-8):4\/LY?H4FU459`6=^RSH#."! +MTIR;R72V,&>P4"B^9@?8I?CZ$V0!K8C1VRIB$JP5,0L`F^&CH2CWLQ@[($6H +MC%7:P_0IYK"8>+A/PN>EHX3)"65Q0BZ[R1,6=OMHB^]-!2RDNF/A;8)KI8B' +M<+`5!4LY)D!NJ)J'(G3(">T(*>T#KIB4\1$TYZ +M0@MZ0NM!3\Q\XOQC8M;'_QZ8"/)Z_H5G3.BFJB?Z')C0@9[0@9[0.>F)MS%V +MA9.>T(&>T'G0$\'/G'],W%SV[X&)D'4^8F**>N+:!"=,@)[0@9[0.>F);8@) +M)SVA`SVA\Z`G+MMU_C'QT%W_'I@(+?PJ3$AX0&P@'JP%J03''Z\?XYA`+#!< +M#*5&V`$/MES`PRC'`V(!,8'C#1QG("[L,-:PA<%88[LBDF&A?.+Q1ME6Q,(! +MHOX18B'L58]8\&'LR;"0!EA(&S_V=,'",B_CC?N]8,'/,>B4L7!2Q,*4QZ#A +MY?YA05OE&Q:BRM%OX%C0MMK"P'=PP8)W_\$5"Q&_.O]89<@RD8;W3/-,0Z/U!PO8\&I:_#/*=]=,ISS-YU?OB/--2+_-,B_V? +M9V)S_Z+.'YMG6C\-\TQR5WWOGUQG>SW_"OLT]G/LWY+==[;Y.+]X-C0F$ONX +ML]W'/HW]&_LS6Q,`/6_',TT#KZVN.P;V?RW8?^C+D)Z$\TSV"GT*SC6ACA^; +M:QIG\R^OD&0?+LJ^WHO-9W*7*7%_/^XGNRCFE3QA8**^/1$&!,!`_;3:_,A- +M4\1`]20PT&S'\TL#K^UWPT`[SBN)&-B'B6-`NP#GC,;FDL9AX*KGSS\&IF_> +MZ.+&@,K[_K?)86`2>D`+>D`+>F!NE1L&VG'>2,3`/O3QQN:.QF%@SJ[SCX'I +MFR>ZN#%PM=?QW^0PH)N$'M"!'M"A'NASQ8`N">>).`9T"W!.:&RN:!P&KJD[ +M_QB8OGFABQL#45[7OR>)@4GH`1WH`1WH@>OBW3#0CO-"(@;VX1S0V-S0.`Q< +M^]+YQ\#TS0-=W!B8ZW7_/QO[`PXD#%B=,&`7YP)'85P@C??+1UWG`G$\0(NO +MK:8PQI?&`7A>#069.\\'X7C`ZC0>*!OE8T'U@RC[Z^H\C@5]G0/,`-EG?,58 +M_V$OXX$?^3_61]GO!ME;IT'V8V-"2?:?$OGN*8T)U5[[/_9OW%^"DL-FC$WIHURO]_K]OR1/H\;&YG(E'6_,Z2*@A+T +M_`8N5_,&AXX7`J_94;<5]/I6LH#U^7#6S_4X?R?U;1H6DR3-]7*9'R`H:SZ7 +M]\UMV`?'Z78/=+MMG-SW?^5<[G3O)YJR/I^V^;MHK^M_;K*OF@;9]WB0 +M?;,H>S;&!]FW<]F?]B#[>4^=']F[CNV_OK+_UI+)R5X[#?T^:L%XV6O%?L_' +M]C1,*_;[XQYD_Q^;SX_L7OZAJ^QUT]'O^\;+7B?V>SZ6IV$ZL=^_[4'V__GX +M^9&]ZQC^ZRO[&*_G?[O)?AKZ_;4)'F0O]GL^A@?9B_U^FP?9:]><']F[CMV_ +MOK+7?>7^/TG^8V-X<0^')'/$`LH=\>`L=S:.W^`TCL?Q>R#;RY'$QNZB?T]E +MBDBVGN?5OX]]6)*YOVOU_\K^_?2OS^N]?O\ZLI_!6(` +MOP<*!QQ\F&HFH\.I!&6->S`Z,TW$F/,!*=U(E!WI@V1Q$[4:>PSX_;?"L/4# +M0C=H0I9#OSHAG]^'>SATR!.S3` +ML[S']FG\;?KV:7C:ES?5?1JV:=NGL<#K^9\H9Y0I]FUK)9^[P7,34=YE6XG2 +MOD$5E+L)^GPWGZ_';[R,Z1\36[D^A)O$-J_DW0M8*\`G8GBQQ/@=L`.[=>7:]B('EB(&;[O&$`5_V[+!Y +M6NG;,!$#D]JO\:#_\[,OKO=MC^9D]^Q(<_-3FY^]V>N9*&Y8J/(="\,>L'!S +MAP^N.7]8<-O'\;7%0H+7\;\K%G1^Z(5W/&`AP>P! +M"WT.+.B<]((X'U2A\Z`7;MM\_K#@MI_C:XN%V[W._[MAP0^]4.`!"[?;QF/A +MV@0G+#CI!7%^J$+G02_SAWT\3T? +M$A:\CR'N5(SAX)"(`QPWA('L<TMV-:,3!M^SONRI@S(T+&SM44Z648#]V`M$`7)-*QLR^H7$X:QMD+#M:I9?V("',:Y$V7.(CR%WAW%X[M+^.US'"7GQ= +MN=B6>.=RD0[>BQ-EHH1V)(SE5V+ME^@`LW-`1@8C($%L]RQ:+*C$MB>Z\E%F +MDO@(\NL`VME`LU#*UU[$XGKP=A?9+D=<'.@GB&O,]W(QSR3>9A;O7I2U`I\9 +M1+I(UIZ0T':[3+%H/&V0,^T5G):D`&W*>%JE,^V5G%:>`;1+QM/:9CO17L5I +M%3E`FSJ>5NU,J^*TX0>!-FT\;:(S[1Q.&U@.M.GC:0N=::_FM$&-0)OA3MO` +M<$"N%W%P#:<-V0>TF1[:%L#RY;11C#90R`+:=1YDX4Q[+<]W;A?09GF0A3/M +M7$X[NP5H-WF0A3/M=9PVLA5H>.N)"',^U_TI`;JT6]V.1."[+G.K&( +MQ-"0F"KXCW3L+-ZQ_(ILWT9](^HP+0U1=XCY-8\KNTCA3*NC(8E-(NV^\;1! +MSK2Q-&1!NDC;,IY6.49+0VY>!S2M]:)N=*KG?*?\XFA(?(Z8GV%\?FIGVODT +MY*Z](FW;>-I$9]IOTY#O[!!IV\?3%CK3+J`A"54B;<YFU&=X;,=I.D,[\`=RG/&K,&2"'Y?>^23=D)D"YS5!&1B_IV?F.?/3V7N[@*8D>Q$MV69&&GB_E,Z= +M286@[`1(,QG-H_#N(+[+;-!04&XT+)7WRQ0-I2YLNC7$!V-'A;/\NW(J[9 +MN0[ZN/G?7G#3S;?$W[KRD1]GK%K]:.::M8^M>WS]AJPG-CZY:7/VEIRGMD(^ +M8_&IZ(L+918Y`7[?A67L9^5"&I=K\CZ%VLL^&K#IQMIA`Y=),HM=\)X*Y,;O +MC^-][%8B.R9/[FD#+]=3'D9('\(SPR$OZ&LR8^T1`\:"ZI.G,)^F<^"(P9@# +M>?*T.]]3]1/=1LPS)<9;GM_E +M@:_[`NT;+DO98Y(G=[&^$Y*<5?D"[=^S-7&&94&\$*`-T`Y]-],JPOYK$3RC9:!@G\'V@X0\@.J(=S/36K']^D7K]Y +MW;H;0HB&_;K$"MN$Y](^]P)M@[H;H`WM!\!3!]P0J$L5E+,,TEOA?RN4TX[G +MV!^6/_(,+;M/">6U8ZP-/,=V:,L26<.5M!KHVM57DWGPWN?0YCWP'^DKQ+JV +M<0P^LAGOH;V&NBMI^7#9?:UX/C>VM0+NH8WM0%LU5+!$!F,X68,CSW*L$]8# +MQY(?`F(%:RK1CA#9AWV$U+Y`&^&JA:OF`WCV/ESMD-X!OQ_`[_MPP;A2A?5^ +M,H_VQ]:0;V+^P,\'>N4K>[#N>,8]#;D_Q>7,=K)7(^D3([N1QU$OBP:KC37]&4+>-IRPN^7 +MAXJ_,.)=#GQ:`?FOV,335A3";SM?R03Y6G%.%<@2Y/\?C7*:"?' +MEDR@+"T*L%7KP,^26:[X^:'-#3]5J*=,\OLR&SA>6H=R,^!^"8%\6G&N$_)@ +M?O/.8S#&S"0*[%,#\B7W(6:Q;+%<7A>H'Z0U8OTV0+YPOP?H&HUD4*H;>Z=L +M+>!1K"_FB>]11[NB\!U>]M(9D(YC4IP;"3#:3-AWV_'7:--#7THBPG`JB541 +M/?2WX%[YDGY='M%C?JC;H1U[&[`O\G;8L1V8_PJP//'&R^];P_@&[Z,\ +M*)2!>138N$Y[9>L(^!`_>MM5/DMKG>4#[S7#^VT4VHCZ!,J,P+AAD%8EE$$> +MV2-L3@;R>1/J4H7Y0'W:Q+SZW\USE;68WQZ4.>O/6UF;0'?]:%L]?]8"-"W4 +MFD1H;BJ/3\:?[2LXR^3;QO7:C^J>W$;['?7^T1(IOH58[WU8'MV2QNPKEH'O +MB_GI,3^)5SJH!^?7T@Q(WX-89'P#/3I4@%C\42J\WUJ)]UOP?ND`T[%CSYN$FV' +M*.O4W[\'_<611VJBFZQ;&!ZW+))AF^'=%C$?UF:&IR*&@0ZZ!=HYAH,':D4Y +M=[CQ1*S[#_LQ;ASJ)/!M;Q^V.O02REY@LD]=V,#MK:BW4OGS*UTPC'5YBN$7 +MZU*0AG5I8?4H4!-X9L;RX=T\L;^*>3V0@GD]#;H*ZV22_["'Z;__PW!PGUCW +M]I$B?'?)PK-R]FX[T+6,\'@S(?!^$\8`."Q_N!EU-,X9P_]=N.<0=(H"\@M" +MC$JX&)`_7(KC`^#U7O!-FI#?B.7H:K)@N&Q9BX1;**<6><_EL\P..&YQ8&N9 +M7>1C$_`Q",K;)K:K">B5UH._ +MDFC1]V.Q>B`-]6`LR)[I/HP7*J9AW^%E/+R,X1#\'@>.'BQQTT^HBZLZ(0_4 +MIX@EU*E\#/)@NMA_I#J?=L7*P_<`5K1.>?=YLG,-7`?6\G<>NDELVU[>-O$> +M^9:/?$L[Z\1'X%O:*I&^!76YH[\^])+4E\?KB[2EV!\<]7HHSTU?M'C6%P\M +M$MO;Z-8W]O"ZB[8%Y-!I&65VY02DH?[$LIUM4#WGJ]B7T^X<+X'^E$W..MHH*T6*/:7M$C7OOA0H=@7I;(/@8ST3N66N)6+ +MLFE#/O`VIGTDV32DAW=SX)U6-_M8A?6#LA8PG?4"TQ4YP[P^A@;13D":7DPK +M%VV)A)LXUSH]'.79/WIH@>0?L;R8+GI8+;8/T[0\[<%^3*OD?*BFS%X]6"[: +M*^Q?H#U!>X3Y7]+F:_[Z+>03\$GP-= +M">HB2/]$_7_!+_@R"/)]N$5\]RCS%;Y,"I#2I/=`-^S"]_!]27^A[K*A;F+E +M+=>*&&?C!LDOLLJ=:#$&#=-IRW.X/EN^!M]E\@-=F7^6?MZPD2CJ!U$OWI?) +MZ[F\C;=C>05K!]>I5;PN]V76C[)GKV-;\-E.IV<8+P.>L;&O^WN"-7,U+M"(_'6^3_V'RA?B?<,]WDD`/[7(@W6B=N(Y6IWG_JP?.7;:)>XK[JR +M3ISW(8:-+X-=6-$OZ1@ZG$0,^;U8QZ.8%@X^@C,-ZJ>W1DUBGBO:)+V!NJ33 +MTBOZ=.F;ZD5=OMG5#TO?[*HS5I:ZZPSPFW<,EZ4GN?@!D#:Q[DB/PCZJ +MRQ+U$YO+2O]4JC/JS:%<:6YCY5/HJ[F5U^*B.[$\*3\%(=B/#!MQ+B:]1-3S +MC2ZZ4K1AJ"==;=5]:UQMULC*]W\673#9(O +MZ]"CZ2FHHZ#\%K'L9I,\N48J&W#4WPGHH67W1<'_:M$'9'US%&,9R9?W`/Y" +M$&>&]8.DSV$KVH8V2/Q]Y$YGO[#N!5HR7/:(RKF-%9"V4YQ#8+QE>:T<:'C! +MM6V\'8^HL1TX)H+R3>CW@,\'_Y=E-(QA_HLO*)]7ZG'DY +M(<1!^AWURB>?W/SXJ@SU^C4_UFY<]>2J3>J5&S=L7I^AGI>AFZ>_.<,]_GN_ +M<.9H_\XS)"A"EK4Q(F?->CZ?]^/2V&J2!<]#&WZ&O'ZD%GX52`?MC(;_05!W +M]7#9CQ>9Y*OWL9C&D`YM4],S[_>#SY:%<9$:SI#(JFRB@"L4+J4<\H-W"DWR +M#!9/''Z5YN`\/2W)TW.Y9(1)<\K)SY!DQ&>O/".4I0&6<9Y63.-T9X@"ZJC$ +M>(\8W2?[C35`O5@]/ +ML-B(,;.;1#Q% +MS?1W1_O%^FY"7?";M=T*;`/8AM"&0:]SA`J<(Z0O.OM8&4Q/H'\U5(1S?AG@ +MCV=$B;ZPA5K3G'RGC&:0C<5+WD%2WAC[&M_A\E[U?7C'[,A_5;1[_I@G_#=+ +MY?!Q]RJ<`S`#=EWFL$6L?<<-:PD_>X%V=5:#'$&^.T^#C81[734!V\'F$`]" +M7AU0=B/T)S:O30&;E0*;'^E@IEQA//$P8?5 +M,1(?`-\'H9R0<&8G!ED>6&C3^(904M6UV(F(5WFJ08XIYD"64H%-4D",MHBV3TW9[*<,=56R,A +M[T4!CN&]Z$CLCX\J/;TW[\[(]M)'*HT_6]<(^R?6XN +M7SL!7E6+OI5E*#<)^L3:-)/\\23'6LS:IT3?P\+MQ=HDL/46KE.3T!?]S\/R +M=15LS)=/M/!_.\X5.=9NUMT+OTK\%L^NQ*OP==T\>>#WA. +MG_2E`!Q:<%T+^I%\N.RQ/2`SME;JE`YZ_;%6#^F@9QX[Z"$]!-('_G][5^^; +M5!3%S^6CMM5&*$U$1'F#)@RVA0*1DIH08XU#!V)(-,:A1JO6P"`,TD5ITJ5. +M'31QT(3!P<'!P3^@I`XF-LK@X";&CW3HT(%$3)I>S[GW`@]X8"I--95?>]^Y +MYW?._7CW7NX]O+RF!KSMQX.$Q8#'>";A;N1QGM>1#U9XG)LU-3>/Y//TQ,/7 +M<`9T)>T4VPSW61<_5DBI[N:P7&J)SM'99#6WFEU'H_PK:T[Z;D(O47\P`QM)N[-B-D$=N(&"L>.@\.<(22E]0RB6JG_5 +MW@%4?T5]96"JO;#.XVR;TAPJ18QP!U,V;L7Z+@EGSC=5_16=ZW5BZG6$Z8JN +M`8_YNG@E\Y"[9L_9&EJUBNM!NF2P/B_:!S-5J[DTK_-]*[OO6&A]$]O&,5W> +M(X6CW1CN+-S05\G6EH=CU;_)LX%L5+#M?_\\Y[VL5_"0;FO]V%/0PNMU#<)X6@'U[=.+DR +M<$5PE?G#<[S-P5L%_5V`'U,`TQ'%]6^C!1.N%@N>J#VP#WKQ9#$LFX6H$R_X +M&X.HT*>[_*[RRXHO*+[8Y7>19XQADCDP0"/+%*HZ_*9\4WWUY1M!?0O*['V, +M[40,P24V]7YRW_ET"Z"8PV0!^#R!J0#P%>/E+\&M3&?[]2H+S2*JL\?"8Q'0@$M.9>:]?MNSF@W9E/)NU=3,ZWZV!$T +M3$>W0(Q,_2C(V!]83GW3\JI4`YMX`FS\#;!0'!B]K>-U`[L\!>SB-+`+'X!- +I?00VF6CFKODD=P_+SZT#2Q>`);R26XP!6UB"?Q-BA'X!UH.@4SA\`@`` +` +end Property changes on: stable/6/sys/dev/mxge/ethp_z8e.dat.gz.uu ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/mxge/if_mxge.c =================================================================== --- stable/6/sys/dev/mxge/if_mxge.c (nonexistent) +++ stable/6/sys/dev/mxge/if_mxge.c (revision 170009) @@ -0,0 +1,3418 @@ +/****************************************************************************** + +Copyright (c) 2006, Myricom Inc. +All rights reserved. + +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. Neither the name of the Myricom Inc, nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +***************************************************************************/ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include /* for pmap_mapdev() */ +#include + +#include +#include +#include + +/* tunable params */ +static int mxge_nvidia_ecrc_enable = 1; +static int mxge_force_firmware = 0; +static int mxge_intr_coal_delay = 30; +static int mxge_deassert_wait = 1; +static int mxge_flow_control = 1; +static int mxge_verbose = 0; +static int mxge_ticks; +static char *mxge_fw_unaligned = "mxge_ethp_z8e"; +static char *mxge_fw_aligned = "mxge_eth_z8e"; + +static int mxge_probe(device_t dev); +static int mxge_attach(device_t dev); +static int mxge_detach(device_t dev); +static int mxge_shutdown(device_t dev); +static void mxge_intr(void *arg); + +static device_method_t mxge_methods[] = +{ + /* Device interface */ + DEVMETHOD(device_probe, mxge_probe), + DEVMETHOD(device_attach, mxge_attach), + DEVMETHOD(device_detach, mxge_detach), + DEVMETHOD(device_shutdown, mxge_shutdown), + {0, 0} +}; + +static driver_t mxge_driver = +{ + "mxge", + mxge_methods, + sizeof(mxge_softc_t), +}; + +static devclass_t mxge_devclass; + +/* Declare ourselves to be a child of the PCI bus.*/ +DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0); +MODULE_DEPEND(mxge, firmware, 1, 1, 1); + +static int mxge_load_firmware(mxge_softc_t *sc); +static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data); + +static int +mxge_probe(device_t dev) +{ + if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) && + (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) { + device_set_desc(dev, "Myri10G-PCIE-8A"); + return 0; + } + return ENXIO; +} + +static void +mxge_enable_wc(mxge_softc_t *sc) +{ + struct mem_range_desc mrdesc; + vm_paddr_t pa; + vm_offset_t len; + int err, action; + + pa = rman_get_start(sc->mem_res); + len = rman_get_size(sc->mem_res); + mrdesc.mr_base = pa; + mrdesc.mr_len = len; + mrdesc.mr_flags = MDF_WRITECOMBINE; + action = MEMRANGE_SET_UPDATE; + strcpy((char *)&mrdesc.mr_owner, "mxge"); + err = mem_range_attr_set(&mrdesc, &action); + if (err != 0) { + device_printf(sc->dev, + "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n", + (unsigned long)pa, (unsigned long)len, err); + } else { + sc->wc = 1; + } +} + + +/* callback to get our DMA address */ +static void +mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, + int error) +{ + if (error == 0) { + *(bus_addr_t *) arg = segs->ds_addr; + } +} + +static int +mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes, + bus_size_t alignment) +{ + int err; + device_t dev = sc->dev; + + /* allocate DMAable memory tags */ + err = bus_dma_tag_create(sc->parent_dmat, /* parent */ + alignment, /* alignment */ + 4096, /* boundary */ + BUS_SPACE_MAXADDR, /* low */ + BUS_SPACE_MAXADDR, /* high */ + NULL, NULL, /* filter */ + bytes, /* maxsize */ + 1, /* num segs */ + 4096, /* maxsegsize */ + BUS_DMA_COHERENT, /* flags */ + NULL, NULL, /* lock */ + &dma->dmat); /* tag */ + if (err != 0) { + device_printf(dev, "couldn't alloc tag (err = %d)\n", err); + return err; + } + + /* allocate DMAable memory & map */ + err = bus_dmamem_alloc(dma->dmat, &dma->addr, + (BUS_DMA_WAITOK | BUS_DMA_COHERENT + | BUS_DMA_ZERO), &dma->map); + if (err != 0) { + device_printf(dev, "couldn't alloc mem (err = %d)\n", err); + goto abort_with_dmat; + } + + /* load the memory */ + err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, + mxge_dmamap_callback, + (void *)&dma->bus_addr, 0); + if (err != 0) { + device_printf(dev, "couldn't load map (err = %d)\n", err); + goto abort_with_mem; + } + return 0; + +abort_with_mem: + bus_dmamem_free(dma->dmat, dma->addr, dma->map); +abort_with_dmat: + (void)bus_dma_tag_destroy(dma->dmat); + return err; +} + + +static void +mxge_dma_free(mxge_dma_t *dma) +{ + bus_dmamap_unload(dma->dmat, dma->map); + bus_dmamem_free(dma->dmat, dma->addr, dma->map); + (void)bus_dma_tag_destroy(dma->dmat); +} + +/* + * The eeprom strings on the lanaiX have the format + * SN=x\0 + * MAC=x:x:x:x:x:x\0 + * PC=text\0 + */ + +static int +mxge_parse_strings(mxge_softc_t *sc) +{ +#define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++) + + char *ptr, *limit; + int i, found_mac; + + ptr = sc->eeprom_strings; + limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE; + found_mac = 0; + while (ptr < limit && *ptr != '\0') { + if (memcmp(ptr, "MAC=", 4) == 0) { + ptr += 1; + sc->mac_addr_string = ptr; + for (i = 0; i < 6; i++) { + ptr += 3; + if ((ptr + 2) > limit) + goto abort; + sc->mac_addr[i] = strtoul(ptr, NULL, 16); + found_mac = 1; + } + } else if (memcmp(ptr, "PC=", 3) == 0) { + ptr += 3; + strncpy(sc->product_code_string, ptr, + sizeof (sc->product_code_string) - 1); + } else if (memcmp(ptr, "SN=", 3) == 0) { + ptr += 3; + strncpy(sc->serial_number_string, ptr, + sizeof (sc->serial_number_string) - 1); + } + MXGE_NEXT_STRING(ptr); + } + + if (found_mac) + return 0; + + abort: + device_printf(sc->dev, "failed to parse eeprom_strings\n"); + + return ENXIO; +} + +#if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ +static void +mxge_enable_nvidia_ecrc(mxge_softc_t *sc) +{ + uint32_t val; + unsigned long base, off; + char *va, *cfgptr; + device_t pdev, mcp55; + uint16_t vendor_id, device_id, word; + uintptr_t bus, slot, func, ivend, idev; + uint32_t *ptr32; + + + if (!mxge_nvidia_ecrc_enable) + return; + + pdev = device_get_parent(device_get_parent(sc->dev)); + if (pdev == NULL) { + device_printf(sc->dev, "could not find parent?\n"); + return; + } + vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2); + device_id = pci_read_config(pdev, PCIR_DEVICE, 2); + + if (vendor_id != 0x10de) + return; + + base = 0; + + if (device_id == 0x005d) { + /* ck804, base address is magic */ + base = 0xe0000000UL; + } else if (device_id >= 0x0374 && device_id <= 0x378) { + /* mcp55, base address stored in chipset */ + mcp55 = pci_find_bsf(0, 0, 0); + if (mcp55 && + 0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) && + 0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) { + word = pci_read_config(mcp55, 0x90, 2); + base = ((unsigned long)word & 0x7ffeU) << 25; + } + } + if (!base) + return; + + /* XXXX + Test below is commented because it is believed that doing + config read/write beyond 0xff will access the config space + for the next larger function. Uncomment this and remove + the hacky pmap_mapdev() way of accessing config space when + FreeBSD grows support for extended pcie config space access + */ +#if 0 + /* See if we can, by some miracle, access the extended + config space */ + val = pci_read_config(pdev, 0x178, 4); + if (val != 0xffffffff) { + val |= 0x40; + pci_write_config(pdev, 0x178, val, 4); + return; + } +#endif + /* Rather than using normal pci config space writes, we must + * map the Nvidia config space ourselves. This is because on + * opteron/nvidia class machine the 0xe000000 mapping is + * handled by the nvidia chipset, that means the internal PCI + * device (the on-chip northbridge), or the amd-8131 bridge + * and things behind them are not visible by this method. + */ + + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_BUS, &bus); + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_SLOT, &slot); + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_FUNCTION, &func); + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_VENDOR, &ivend); + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_DEVICE, &idev); + + off = base + + 0x00100000UL * (unsigned long)bus + + 0x00001000UL * (unsigned long)(func + + 8 * slot); + + /* map it into the kernel */ + va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); + + + if (va == NULL) { + device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); + return; + } + /* get a pointer to the config space mapped into the kernel */ + cfgptr = va + (off & PAGE_MASK); + + /* make sure that we can really access it */ + vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); + device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); + if (! (vendor_id == ivend && device_id == idev)) { + device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", + vendor_id, device_id); + pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); + return; + } + + ptr32 = (uint32_t*)(cfgptr + 0x178); + val = *ptr32; + + if (val == 0xffffffff) { + device_printf(sc->dev, "extended mapping failed\n"); + pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); + return; + } + *ptr32 = val | 0x40; + pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); + if (mxge_verbose) + device_printf(sc->dev, + "Enabled ECRC on upstream Nvidia bridge " + "at %d:%d:%d\n", + (int)bus, (int)slot, (int)func); + return; +} +#else +static void +mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) +{ + device_printf(sc->dev, + "Nforce 4 chipset on non-x86/amd64!?!?!\n"); + return; +} +#endif + + +static int +mxge_dma_test(mxge_softc_t *sc, int test_type) +{ + mxge_cmd_t cmd; + bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr; + int status; + uint32_t len; + char *test = " "; + + + /* Run a small DMA test. + * The magic multipliers to the length tell the firmware + * to do DMA read, write, or read+write tests. The + * results are returned in cmd.data0. The upper 16 + * bits of the return is the number of transfers completed. + * The lower 16 bits is the time in 0.5us ticks that the + * transfers took to complete. + */ + + len = sc->tx.boundary; + + cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); + cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); + cmd.data2 = len * 0x10000; + status = mxge_send_cmd(sc, test_type, &cmd); + if (status != 0) { + test = "read"; + goto abort; + } + sc->read_dma = ((cmd.data0>>16) * len * 2) / + (cmd.data0 & 0xffff); + cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); + cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); + cmd.data2 = len * 0x1; + status = mxge_send_cmd(sc, test_type, &cmd); + if (status != 0) { + test = "write"; + goto abort; + } + sc->write_dma = ((cmd.data0>>16) * len * 2) / + (cmd.data0 & 0xffff); + + cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); + cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); + cmd.data2 = len * 0x10001; + status = mxge_send_cmd(sc, test_type, &cmd); + if (status != 0) { + test = "read/write"; + goto abort; + } + sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) / + (cmd.data0 & 0xffff); + +abort: + if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) + device_printf(sc->dev, "DMA %s benchmark failed: %d\n", + test, status); + + return status; +} + +/* + * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput + * when the PCI-E Completion packets are aligned on an 8-byte + * boundary. Some PCI-E chip sets always align Completion packets; on + * the ones that do not, the alignment can be enforced by enabling + * ECRC generation (if supported). + * + * When PCI-E Completion packets are not aligned, it is actually more + * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. + * + * If the driver can neither enable ECRC nor verify that it has + * already been enabled, then it must use a firmware image which works + * around unaligned completion packets (ethp_z8e.dat), and it should + * also ensure that it never gives the device a Read-DMA which is + * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is + * enabled, then the driver should use the aligned (eth_z8e.dat) + * firmware image, and set tx.boundary to 4KB. + */ + +static int +mxge_firmware_probe(mxge_softc_t *sc) +{ + device_t dev = sc->dev; + int reg, status; + uint16_t pectl; + + sc->tx.boundary = 4096; + /* + * Verify the max read request size was set to 4KB + * before trying the test with 4KB. + */ + if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { + pectl = pci_read_config(dev, reg + 0x8, 2); + if ((pectl & (5 << 12)) != (5 << 12)) { + device_printf(dev, "Max Read Req. size != 4k (0x%x\n", + pectl); + sc->tx.boundary = 2048; + } + } + + /* + * load the optimized firmware (which assumes aligned PCIe + * completions) in order to see if it works on this host. + */ + sc->fw_name = mxge_fw_aligned; + status = mxge_load_firmware(sc); + if (status != 0) { + return status; + } + + /* + * Enable ECRC if possible + */ + mxge_enable_nvidia_ecrc(sc); + + /* + * Run a DMA test which watches for unaligned completions and + * aborts on the first one seen. + */ + + status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST); + if (status == 0) + return 0; /* keep the aligned firmware */ + + if (status != E2BIG) + device_printf(dev, "DMA test failed: %d\n", status); + if (status == ENOSYS) + device_printf(dev, "Falling back to ethp! " + "Please install up to date fw\n"); + return status; +} + +static int +mxge_select_firmware(mxge_softc_t *sc) +{ + int aligned = 0; + + + if (mxge_force_firmware != 0) { + if (mxge_force_firmware == 1) + aligned = 1; + else + aligned = 0; + if (mxge_verbose) + device_printf(sc->dev, + "Assuming %s completions (forced)\n", + aligned ? "aligned" : "unaligned"); + goto abort; + } + + /* if the PCIe link width is 4 or less, we can use the aligned + firmware and skip any checks */ + if (sc->link_width != 0 && sc->link_width <= 4) { + device_printf(sc->dev, + "PCIe x%d Link, expect reduced performance\n", + sc->link_width); + aligned = 1; + goto abort; + } + + if (0 == mxge_firmware_probe(sc)) + return 0; + +abort: + if (aligned) { + sc->fw_name = mxge_fw_aligned; + sc->tx.boundary = 4096; + } else { + sc->fw_name = mxge_fw_unaligned; + sc->tx.boundary = 2048; + } + return (mxge_load_firmware(sc)); +} + +union qualhack +{ + const char *ro_char; + char *rw_char; +}; + +static int +mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr) +{ + + + if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { + device_printf(sc->dev, "Bad firmware type: 0x%x\n", + be32toh(hdr->mcp_type)); + return EIO; + } + + /* save firmware version for sysctl */ + strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version)); + if (mxge_verbose) + device_printf(sc->dev, "firmware id: %s\n", hdr->version); + + sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major, + &sc->fw_ver_minor, &sc->fw_ver_tiny); + + if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR + && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) { + device_printf(sc->dev, "Found firmware version %s\n", + sc->fw_version); + device_printf(sc->dev, "Driver needs %d.%d\n", + MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); + return EINVAL; + } + return 0; + +} + +static int +mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit) +{ + const struct firmware *fw; + const mcp_gen_header_t *hdr; + unsigned hdr_offset; + const char *fw_data; + union qualhack hack; + int status; + unsigned int i; + char dummy; + + + fw = firmware_get(sc->fw_name); + + if (fw == NULL) { + device_printf(sc->dev, "Could not find firmware image %s\n", + sc->fw_name); + return ENOENT; + } + if (fw->datasize > *limit || + fw->datasize < MCP_HEADER_PTR_OFFSET + 4) { + device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n", + sc->fw_name, (int)fw->datasize, (int) *limit); + status = ENOSPC; + goto abort_with_fw; + } + *limit = fw->datasize; + + /* check id */ + fw_data = (const char *)fw->data; + hdr_offset = htobe32(*(const uint32_t *) + (fw_data + MCP_HEADER_PTR_OFFSET)); + if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) { + device_printf(sc->dev, "Bad firmware file"); + status = EIO; + goto abort_with_fw; + } + hdr = (const void*)(fw_data + hdr_offset); + + status = mxge_validate_firmware(sc, hdr); + if (status != 0) + goto abort_with_fw; + + hack.ro_char = fw_data; + /* Copy the inflated firmware to NIC SRAM. */ + for (i = 0; i < *limit; i += 256) { + mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, + hack.rw_char + i, + min(256U, (unsigned)(*limit - i))); + mb(); + dummy = *sc->sram; + mb(); + } + + status = 0; +abort_with_fw: + firmware_put(fw, FIRMWARE_UNLOAD); + return status; +} + +/* + * Enable or disable periodic RDMAs from the host to make certain + * chipsets resend dropped PCIe messages + */ + +static void +mxge_dummy_rdma(mxge_softc_t *sc, int enable) +{ + char buf_bytes[72]; + volatile uint32_t *confirm; + volatile char *submit; + uint32_t *buf, dma_low, dma_high; + int i; + + buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); + + /* clear confirmation addr */ + confirm = (volatile uint32_t *)sc->cmd; + *confirm = 0; + mb(); + + /* send an rdma command to the PCIe engine, and wait for the + response in the confirmation address. The firmware should + write a -1 there to indicate it is alive and well + */ + + dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); + dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); + buf[0] = htobe32(dma_high); /* confirm addr MSW */ + buf[1] = htobe32(dma_low); /* confirm addr LSW */ + buf[2] = htobe32(0xffffffff); /* confirm data */ + dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); + dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); + buf[3] = htobe32(dma_high); /* dummy addr MSW */ + buf[4] = htobe32(dma_low); /* dummy addr LSW */ + buf[5] = htobe32(enable); /* enable? */ + + + submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA); + + mxge_pio_copy(submit, buf, 64); + mb(); + DELAY(1000); + mb(); + i = 0; + while (*confirm != 0xffffffff && i < 20) { + DELAY(1000); + i++; + } + if (*confirm != 0xffffffff) { + device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", + (enable ? "enable" : "disable"), confirm, + *confirm); + } + return; +} + +static int +mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data) +{ + mcp_cmd_t *buf; + char buf_bytes[sizeof(*buf) + 8]; + volatile mcp_cmd_response_t *response = sc->cmd; + volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD; + uint32_t dma_low, dma_high; + int err, sleep_total = 0; + + /* ensure buf is aligned to 8 bytes */ + buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); + + buf->data0 = htobe32(data->data0); + buf->data1 = htobe32(data->data1); + buf->data2 = htobe32(data->data2); + buf->cmd = htobe32(cmd); + dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); + dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); + + buf->response_addr.low = htobe32(dma_low); + buf->response_addr.high = htobe32(dma_high); + mtx_lock(&sc->cmd_mtx); + response->result = 0xffffffff; + mb(); + mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); + + /* wait up to 20ms */ + err = EAGAIN; + for (sleep_total = 0; sleep_total < 20; sleep_total++) { + bus_dmamap_sync(sc->cmd_dma.dmat, + sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); + mb(); + switch (be32toh(response->result)) { + case 0: + data->data0 = be32toh(response->data); + err = 0; + break; + case 0xffffffff: + DELAY(1000); + break; + case MXGEFW_CMD_UNKNOWN: + err = ENOSYS; + break; + case MXGEFW_CMD_ERROR_UNALIGNED: + err = E2BIG; + break; + default: + device_printf(sc->dev, + "mxge: command %d " + "failed, result = %d\n", + cmd, be32toh(response->result)); + err = ENXIO; + break; + } + if (err != EAGAIN) + break; + } + if (err == EAGAIN) + device_printf(sc->dev, "mxge: command %d timed out" + "result = %d\n", + cmd, be32toh(response->result)); + mtx_unlock(&sc->cmd_mtx); + return err; +} + +static int +mxge_adopt_running_firmware(mxge_softc_t *sc) +{ + struct mcp_gen_header *hdr; + const size_t bytes = sizeof (struct mcp_gen_header); + size_t hdr_offset; + int status; + + /* find running firmware header */ + hdr_offset = htobe32(*(volatile uint32_t *) + (sc->sram + MCP_HEADER_PTR_OFFSET)); + + if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) { + device_printf(sc->dev, + "Running firmware has bad header offset (%d)\n", + (int)hdr_offset); + return EIO; + } + + /* copy header of running firmware from SRAM to host memory to + * validate firmware */ + hdr = malloc(bytes, M_DEVBUF, M_NOWAIT); + if (hdr == NULL) { + device_printf(sc->dev, "could not malloc firmware hdr\n"); + return ENOMEM; + } + bus_space_read_region_1(rman_get_bustag(sc->mem_res), + rman_get_bushandle(sc->mem_res), + hdr_offset, (char *)hdr, bytes); + status = mxge_validate_firmware(sc, hdr); + free(hdr, M_DEVBUF); + + /* + * check to see if adopted firmware has bug where adopting + * it will cause broadcasts to be filtered unless the NIC + * is kept in ALLMULTI mode + */ + if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 && + sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) { + sc->adopted_rx_filter_bug = 1; + device_printf(sc->dev, "Adopting fw %d.%d.%d: " + "working around rx filter bug\n", + sc->fw_ver_major, sc->fw_ver_minor, + sc->fw_ver_tiny); + } + + return status; +} + + +static int +mxge_load_firmware(mxge_softc_t *sc) +{ + volatile uint32_t *confirm; + volatile char *submit; + char buf_bytes[72]; + uint32_t *buf, size, dma_low, dma_high; + int status, i; + + buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); + + size = sc->sram_size; + status = mxge_load_firmware_helper(sc, &size); + if (status) { + /* Try to use the currently running firmware, if + it is new enough */ + status = mxge_adopt_running_firmware(sc); + if (status) { + device_printf(sc->dev, + "failed to adopt running firmware\n"); + return status; + } + device_printf(sc->dev, + "Successfully adopted running firmware\n"); + if (sc->tx.boundary == 4096) { + device_printf(sc->dev, + "Using firmware currently running on NIC" + ". For optimal\n"); + device_printf(sc->dev, + "performance consider loading optimized " + "firmware\n"); + } + sc->fw_name = mxge_fw_unaligned; + sc->tx.boundary = 2048; + return 0; + } + /* clear confirmation addr */ + confirm = (volatile uint32_t *)sc->cmd; + *confirm = 0; + mb(); + /* send a reload command to the bootstrap MCP, and wait for the + response in the confirmation address. The firmware should + write a -1 there to indicate it is alive and well + */ + + dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); + dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); + + buf[0] = htobe32(dma_high); /* confirm addr MSW */ + buf[1] = htobe32(dma_low); /* confirm addr LSW */ + buf[2] = htobe32(0xffffffff); /* confirm data */ + + /* FIX: All newest firmware should un-protect the bottom of + the sram before handoff. However, the very first interfaces + do not. Therefore the handoff copy must skip the first 8 bytes + */ + /* where the code starts*/ + buf[3] = htobe32(MXGE_FW_OFFSET + 8); + buf[4] = htobe32(size - 8); /* length of code */ + buf[5] = htobe32(8); /* where to copy to */ + buf[6] = htobe32(0); /* where to jump to */ + + submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF); + mxge_pio_copy(submit, buf, 64); + mb(); + DELAY(1000); + mb(); + i = 0; + while (*confirm != 0xffffffff && i < 20) { + DELAY(1000*10); + i++; + bus_dmamap_sync(sc->cmd_dma.dmat, + sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); + } + if (*confirm != 0xffffffff) { + device_printf(sc->dev,"handoff failed (%p = 0x%x)", + confirm, *confirm); + + return ENXIO; + } + return 0; +} + +static int +mxge_update_mac_address(mxge_softc_t *sc) +{ + mxge_cmd_t cmd; + uint8_t *addr = sc->mac_addr; + int status; + + + cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) + | (addr[2] << 8) | addr[3]); + + cmd.data1 = ((addr[4] << 8) | (addr[5])); + + status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd); + return status; +} + +static int +mxge_change_pause(mxge_softc_t *sc, int pause) +{ + mxge_cmd_t cmd; + int status; + + if (pause) + status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, + &cmd); + else + status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, + &cmd); + + if (status) { + device_printf(sc->dev, "Failed to set flow control mode\n"); + return ENXIO; + } + sc->pause = pause; + return 0; +} + +static void +mxge_change_promisc(mxge_softc_t *sc, int promisc) +{ + mxge_cmd_t cmd; + int status; + + if (promisc) + status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, + &cmd); + else + status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, + &cmd); + + if (status) { + device_printf(sc->dev, "Failed to set promisc mode\n"); + } +} + +static void +mxge_set_multicast_list(mxge_softc_t *sc) +{ + mxge_cmd_t cmd; + struct ifmultiaddr *ifma; + struct ifnet *ifp = sc->ifp; + int err; + + /* This firmware is known to not support multicast */ + if (!sc->fw_multicast_support) + return; + + /* Disable multicast filtering while we play with the lists*/ + err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd); + if (err != 0) { + device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI," + " error status: %d\n", err); + return; + } + + if (sc->adopted_rx_filter_bug) + return; + + if (ifp->if_flags & IFF_ALLMULTI) + /* request to disable multicast filtering, so quit here */ + return; + + /* Flush all the filters */ + + err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd); + if (err != 0) { + device_printf(sc->dev, + "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS" + ", error status: %d\n", err); + return; + } + + /* Walk the multicast list, and add each address */ + + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + &cmd.data0, 4); + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4, + &cmd.data1, 2); + cmd.data0 = htonl(cmd.data0); + cmd.data1 = htonl(cmd.data1); + err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd); + if (err != 0) { + device_printf(sc->dev, "Failed " + "MXGEFW_JOIN_MULTICAST_GROUP, error status:" + "%d\t", err); + /* abort, leaving multicast filtering off */ + IF_ADDR_UNLOCK(ifp); + return; + } + } + IF_ADDR_UNLOCK(ifp); + /* Enable multicast filtering */ + err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd); + if (err != 0) { + device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI" + ", error status: %d\n", err); + } +} + +static int +mxge_max_mtu(mxge_softc_t *sc) +{ + mxge_cmd_t cmd; + int status; + + if (MJUMPAGESIZE - MXGEFW_PAD > MXGEFW_MAX_MTU) + return MXGEFW_MAX_MTU - MXGEFW_PAD; + + /* try to set nbufs to see if it we can + use virtually contiguous jumbos */ + cmd.data0 = 0; + status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, + &cmd); + if (status == 0) + return MXGEFW_MAX_MTU - MXGEFW_PAD; + + /* otherwise, we're limited to MJUMPAGESIZE */ + return MJUMPAGESIZE - MXGEFW_PAD; +} + +static int +mxge_reset(mxge_softc_t *sc, int interrupts_setup) +{ + + mxge_cmd_t cmd; + size_t bytes; + int status; + + /* try to send a reset command to the card to see if it + is alive */ + memset(&cmd, 0, sizeof (cmd)); + status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); + if (status != 0) { + device_printf(sc->dev, "failed reset\n"); + return ENXIO; + } + + mxge_dummy_rdma(sc, 1); + + if (interrupts_setup) { + /* Now exchange information about interrupts */ + bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry); + memset(sc->rx_done.entry, 0, bytes); + cmd.data0 = (uint32_t)bytes; + status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd); + cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr); + cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr); + status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd); + } + + status |= mxge_send_cmd(sc, + MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd); + + + sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0); + + status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd); + sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); + + + status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, + &cmd); + sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0); + if (status != 0) { + device_printf(sc->dev, "failed set interrupt parameters\n"); + return status; + } + + + *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay); + + + /* run a DMA benchmark */ + (void) mxge_dma_test(sc, MXGEFW_DMA_TEST); + + /* reset mcp/driver shared state back to 0 */ + sc->rx_done.idx = 0; + sc->rx_done.cnt = 0; + sc->tx.req = 0; + sc->tx.done = 0; + sc->tx.pkt_done = 0; + sc->tx.wake = 0; + sc->tx_defrag = 0; + sc->tx.stall = 0; + sc->rx_big.cnt = 0; + sc->rx_small.cnt = 0; + sc->rdma_tags_available = 15; + sc->fw_stats->valid = 0; + sc->fw_stats->send_done_count = 0; + sc->lro_bad_csum = 0; + sc->lro_queued = 0; + sc->lro_flushed = 0; + status = mxge_update_mac_address(sc); + mxge_change_promisc(sc, 0); + mxge_change_pause(sc, sc->pause); + mxge_set_multicast_list(sc); + return status; +} + +static int +mxge_change_intr_coal(SYSCTL_HANDLER_ARGS) +{ + mxge_softc_t *sc; + unsigned int intr_coal_delay; + int err; + + sc = arg1; + intr_coal_delay = sc->intr_coal_delay; + err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); + if (err != 0) { + return err; + } + if (intr_coal_delay == sc->intr_coal_delay) + return 0; + + if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) + return EINVAL; + + mtx_lock(&sc->driver_mtx); + *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay); + sc->intr_coal_delay = intr_coal_delay; + + mtx_unlock(&sc->driver_mtx); + return err; +} + +static int +mxge_change_flow_control(SYSCTL_HANDLER_ARGS) +{ + mxge_softc_t *sc; + unsigned int enabled; + int err; + + sc = arg1; + enabled = sc->pause; + err = sysctl_handle_int(oidp, &enabled, arg2, req); + if (err != 0) { + return err; + } + if (enabled == sc->pause) + return 0; + + mtx_lock(&sc->driver_mtx); + err = mxge_change_pause(sc, enabled); + mtx_unlock(&sc->driver_mtx); + return err; +} + +static int +mxge_handle_be32(SYSCTL_HANDLER_ARGS) +{ + int err; + + if (arg1 == NULL) + return EFAULT; + arg2 = be32toh(*(int *)arg1); + arg1 = NULL; + err = sysctl_handle_int(oidp, arg1, arg2, req); + + return err; +} + +static void +mxge_add_sysctls(mxge_softc_t *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *children; + mcp_irq_data_t *fw; + + ctx = device_get_sysctl_ctx(sc->dev); + children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); + fw = sc->fw_stats; + + /* random information */ + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, + "firmware_version", + CTLFLAG_RD, &sc->fw_version, + 0, "firmware version"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, + "serial_number", + CTLFLAG_RD, &sc->serial_number_string, + 0, "serial number"); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, + "product_code", + CTLFLAG_RD, &sc->product_code_string, + 0, "product_code"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "pcie_link_width", + CTLFLAG_RD, &sc->link_width, + 0, "tx_boundary"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "tx_boundary", + CTLFLAG_RD, &sc->tx.boundary, + 0, "tx_boundary"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "write_combine", + CTLFLAG_RD, &sc->wc, + 0, "write combining PIO?"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "read_dma_MBs", + CTLFLAG_RD, &sc->read_dma, + 0, "DMA Read speed in MB/s"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "write_dma_MBs", + CTLFLAG_RD, &sc->write_dma, + 0, "DMA Write speed in MB/s"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "read_write_dma_MBs", + CTLFLAG_RD, &sc->read_write_dma, + 0, "DMA concurrent Read/Write speed in MB/s"); + + + /* performance related tunables */ + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "intr_coal_delay", + CTLTYPE_INT|CTLFLAG_RW, sc, + 0, mxge_change_intr_coal, + "I", "interrupt coalescing delay in usecs"); + + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "flow_control_enabled", + CTLTYPE_INT|CTLFLAG_RW, sc, + 0, mxge_change_flow_control, + "I", "interrupt coalescing delay in usecs"); + + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "deassert_wait", + CTLFLAG_RW, &mxge_deassert_wait, + 0, "Wait for IRQ line to go low in ihandler"); + + /* stats block from firmware is in network byte order. + Need to swap it */ + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "link_up", + CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, + 0, mxge_handle_be32, + "I", "link up"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "rdma_tags_available", + CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, + 0, mxge_handle_be32, + "I", "rdma_tags_available"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_bad_crc32", + CTLTYPE_INT|CTLFLAG_RD, + &fw->dropped_bad_crc32, + 0, mxge_handle_be32, + "I", "dropped_bad_crc32"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_bad_phy", + CTLTYPE_INT|CTLFLAG_RD, + &fw->dropped_bad_phy, + 0, mxge_handle_be32, + "I", "dropped_bad_phy"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_link_error_or_filtered", + CTLTYPE_INT|CTLFLAG_RD, + &fw->dropped_link_error_or_filtered, + 0, mxge_handle_be32, + "I", "dropped_link_error_or_filtered"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_link_overflow", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, + 0, mxge_handle_be32, + "I", "dropped_link_overflow"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_multicast_filtered", + CTLTYPE_INT|CTLFLAG_RD, + &fw->dropped_multicast_filtered, + 0, mxge_handle_be32, + "I", "dropped_multicast_filtered"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_no_big_buffer", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, + 0, mxge_handle_be32, + "I", "dropped_no_big_buffer"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_no_small_buffer", + CTLTYPE_INT|CTLFLAG_RD, + &fw->dropped_no_small_buffer, + 0, mxge_handle_be32, + "I", "dropped_no_small_buffer"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_overrun", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, + 0, mxge_handle_be32, + "I", "dropped_overrun"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_pause", + CTLTYPE_INT|CTLFLAG_RD, + &fw->dropped_pause, + 0, mxge_handle_be32, + "I", "dropped_pause"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_runt", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, + 0, mxge_handle_be32, + "I", "dropped_runt"); + + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_unicast_filtered", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, + 0, mxge_handle_be32, + "I", "dropped_unicast_filtered"); + + /* host counters exported for debugging */ + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "rx_small_cnt", + CTLFLAG_RD, &sc->rx_small.cnt, + 0, "rx_small_cnt"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "rx_big_cnt", + CTLFLAG_RD, &sc->rx_big.cnt, + 0, "rx_small_cnt"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "tx_req", + CTLFLAG_RD, &sc->tx.req, + 0, "tx_req"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "tx_done", + CTLFLAG_RD, &sc->tx.done, + 0, "tx_done"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "tx_pkt_done", + CTLFLAG_RD, &sc->tx.pkt_done, + 0, "tx_done"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "tx_stall", + CTLFLAG_RD, &sc->tx.stall, + 0, "tx_stall"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "tx_wake", + CTLFLAG_RD, &sc->tx.wake, + 0, "tx_wake"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "tx_defrag", + CTLFLAG_RD, &sc->tx_defrag, + 0, "tx_defrag"); + + /* verbose printing? */ + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "verbose", + CTLFLAG_RW, &mxge_verbose, + 0, "verbose printing"); + + /* lro */ + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "lro_cnt", CTLFLAG_RD, &sc->lro_cnt, + 0, "number of lro merge queues"); + + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "lro_flushed", CTLFLAG_RD, &sc->lro_flushed, + 0, "number of lro merge queues flushed"); + + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "lro_queued", CTLFLAG_RD, &sc->lro_queued, + 0, "number of frames appended to lro merge queues"); + +} + +/* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy + backwards one at a time and handle ring wraps */ + +static inline void +mxge_submit_req_backwards(mxge_tx_buf_t *tx, + mcp_kreq_ether_send_t *src, int cnt) +{ + int idx, starting_slot; + starting_slot = tx->req; + while (cnt > 1) { + cnt--; + idx = (starting_slot + cnt) & tx->mask; + mxge_pio_copy(&tx->lanai[idx], + &src[cnt], sizeof(*src)); + mb(); + } +} + +/* + * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy + * at most 32 bytes at a time, so as to avoid involving the software + * pio handler in the nic. We re-write the first segment's flags + * to mark them valid only after writing the entire chain + */ + +static inline void +mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, + int cnt) +{ + int idx, i; + uint32_t *src_ints; + volatile uint32_t *dst_ints; + mcp_kreq_ether_send_t *srcp; + volatile mcp_kreq_ether_send_t *dstp, *dst; + uint8_t last_flags; + + idx = tx->req & tx->mask; + + last_flags = src->flags; + src->flags = 0; + mb(); + dst = dstp = &tx->lanai[idx]; + srcp = src; + + if ((idx + cnt) < tx->mask) { + for (i = 0; i < (cnt - 1); i += 2) { + mxge_pio_copy(dstp, srcp, 2 * sizeof(*src)); + mb(); /* force write every 32 bytes */ + srcp += 2; + dstp += 2; + } + } else { + /* submit all but the first request, and ensure + that it is submitted below */ + mxge_submit_req_backwards(tx, src, cnt); + i = 0; + } + if (i < cnt) { + /* submit the first request */ + mxge_pio_copy(dstp, srcp, sizeof(*src)); + mb(); /* barrier before setting valid flag */ + } + + /* re-write the last 32-bits with the valid flags */ + src->flags = last_flags; + src_ints = (uint32_t *)src; + src_ints+=3; + dst_ints = (volatile uint32_t *)dst; + dst_ints+=3; + *dst_ints = *src_ints; + tx->req += cnt; + mb(); +} + +static void +mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt, + int ip_off) +{ + mxge_tx_buf_t *tx; + mcp_kreq_ether_send_t *req; + bus_dma_segment_t *seg; + struct ip *ip; + struct tcphdr *tcp; + uint32_t low, high_swapped; + int len, seglen, cum_len, cum_len_next; + int next_is_first, chop, cnt, rdma_count, small; + uint16_t pseudo_hdr_offset, cksum_offset, mss; + uint8_t flags, flags_next; + static int once; + + mss = m->m_pkthdr.tso_segsz; + + /* negative cum_len signifies to the + * send loop that we are still in the + * header portion of the TSO packet. + */ + + /* ensure we have the ethernet, IP and TCP + header together in the first mbuf, copy + it to a scratch buffer if not */ + if (__predict_false(m->m_len < ip_off + sizeof (*ip))) { + m_copydata(m, 0, ip_off + sizeof (*ip), + sc->scratch); + ip = (struct ip *)(sc->scratch + ip_off); + } else { + ip = (struct ip *)(mtod(m, char *) + ip_off); + } + if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2) + + sizeof (*tcp))) { + m_copydata(m, 0, ip_off + (ip->ip_hl << 2) + + sizeof (*tcp), sc->scratch); + ip = (struct ip *)(mtod(m, char *) + ip_off); + } + + tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2)); + cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2)); + + /* TSO implies checksum offload on this hardware */ + cksum_offset = ip_off + (ip->ip_hl << 2); + flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST; + + + /* for TSO, pseudo_hdr_offset holds mss. + * The firmware figures out where to put + * the checksum by parsing the header. */ + pseudo_hdr_offset = htobe16(mss); + + tx = &sc->tx; + req = tx->req_list; + seg = tx->seg_list; + cnt = 0; + rdma_count = 0; + /* "rdma_count" is the number of RDMAs belonging to the + * current packet BEFORE the current send request. For + * non-TSO packets, this is equal to "count". + * For TSO packets, rdma_count needs to be reset + * to 0 after a segment cut. + * + * The rdma_count field of the send request is + * the number of RDMAs of the packet starting at + * that request. For TSO send requests with one ore more cuts + * in the middle, this is the number of RDMAs starting + * after the last cut in the request. All previous + * segments before the last cut implicitly have 1 RDMA. + * + * Since the number of RDMAs is not known beforehand, + * it must be filled-in retroactively - after each + * segmentation cut or at the end of the entire packet. + */ + + while (busdma_seg_cnt) { + /* Break the busdma segment up into pieces*/ + low = MXGE_LOWPART_TO_U32(seg->ds_addr); + high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); + len = seg->ds_len; + + while (len) { + flags_next = flags & ~MXGEFW_FLAGS_FIRST; + seglen = len; + cum_len_next = cum_len + seglen; + (req-rdma_count)->rdma_count = rdma_count + 1; + if (__predict_true(cum_len >= 0)) { + /* payload */ + chop = (cum_len_next > mss); + cum_len_next = cum_len_next % mss; + next_is_first = (cum_len_next == 0); + flags |= chop * MXGEFW_FLAGS_TSO_CHOP; + flags_next |= next_is_first * + MXGEFW_FLAGS_FIRST; + rdma_count |= -(chop | next_is_first); + rdma_count += chop & !next_is_first; + } else if (cum_len_next >= 0) { + /* header ends */ + rdma_count = -1; + cum_len_next = 0; + seglen = -cum_len; + small = (mss <= MXGEFW_SEND_SMALL_SIZE); + flags_next = MXGEFW_FLAGS_TSO_PLD | + MXGEFW_FLAGS_FIRST | + (small * MXGEFW_FLAGS_SMALL); + } + + req->addr_high = high_swapped; + req->addr_low = htobe32(low); + req->pseudo_hdr_offset = pseudo_hdr_offset; + req->pad = 0; + req->rdma_count = 1; + req->length = htobe16(seglen); + req->cksum_offset = cksum_offset; + req->flags = flags | ((cum_len & 1) * + MXGEFW_FLAGS_ALIGN_ODD); + low += seglen; + len -= seglen; + cum_len = cum_len_next; + flags = flags_next; + req++; + cnt++; + rdma_count++; + if (__predict_false(cksum_offset > seglen)) + cksum_offset -= seglen; + else + cksum_offset = 0; + if (__predict_false(cnt > tx->max_desc)) + goto drop; + } + busdma_seg_cnt--; + seg++; + } + (req-rdma_count)->rdma_count = rdma_count; + + do { + req--; + req->flags |= MXGEFW_FLAGS_TSO_LAST; + } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST))); + + tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; + mxge_submit_req(tx, tx->req_list, cnt); + return; + +drop: + bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map); + m_freem(m); + sc->ifp->if_oerrors++; + if (!once) { + printf("tx->max_desc exceeded via TSO!\n"); + printf("mss = %d, %ld, %d!\n", mss, + (long)seg - (long)tx->seg_list, tx->max_desc); + once = 1; + } + return; + +} + +/* + * We reproduce the software vlan tag insertion from + * net/if_vlan.c:vlan_start() here so that we can advertise "hardware" + * vlan tag insertion. We need to advertise this in order to have the + * vlan interface respect our csum offload flags. + */ +static struct mbuf * +mxge_vlan_tag_insert(struct mbuf *m) +{ + struct ether_vlan_header *evl; + + M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT); + if (__predict_false(m == NULL)) + return NULL; + if (m->m_len < sizeof(*evl)) { + m = m_pullup(m, sizeof(*evl)); + if (__predict_false(m == NULL)) + return NULL; + } + /* + * Transform the Ethernet header into an Ethernet header + * with 802.1Q encapsulation. + */ + evl = mtod(m, struct ether_vlan_header *); + bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN, + (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN); + evl->evl_encap_proto = htons(ETHERTYPE_VLAN); + evl->evl_tag = htons(m->m_pkthdr.ether_vtag); + m->m_flags &= ~M_VLANTAG; + return m; +} + +static void +mxge_encap(mxge_softc_t *sc, struct mbuf *m) +{ + mcp_kreq_ether_send_t *req; + bus_dma_segment_t *seg; + struct mbuf *m_tmp; + struct ifnet *ifp; + mxge_tx_buf_t *tx; + struct ip *ip; + int cnt, cum_len, err, i, idx, odd_flag, ip_off; + uint16_t pseudo_hdr_offset; + uint8_t flags, cksum_offset; + + + + ifp = sc->ifp; + tx = &sc->tx; + + ip_off = sizeof (struct ether_header); + if (m->m_flags & M_VLANTAG) { + m = mxge_vlan_tag_insert(m); + if (__predict_false(m == NULL)) + goto drop; + ip_off += ETHER_VLAN_ENCAP_LEN; + } + + /* (try to) map the frame for DMA */ + idx = tx->req & tx->mask; + err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, + m, tx->seg_list, &cnt, + BUS_DMA_NOWAIT); + if (__predict_false(err == EFBIG)) { + /* Too many segments in the chain. Try + to defrag */ + m_tmp = m_defrag(m, M_NOWAIT); + if (m_tmp == NULL) { + goto drop; + } + sc->tx_defrag++; + m = m_tmp; + err = bus_dmamap_load_mbuf_sg(tx->dmat, + tx->info[idx].map, + m, tx->seg_list, &cnt, + BUS_DMA_NOWAIT); + } + if (__predict_false(err != 0)) { + device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d" + " packet len = %d\n", err, m->m_pkthdr.len); + goto drop; + } + bus_dmamap_sync(tx->dmat, tx->info[idx].map, + BUS_DMASYNC_PREWRITE); + tx->info[idx].m = m; + + + /* TSO is different enough, we handle it in another routine */ + if (m->m_pkthdr.csum_flags & (CSUM_TSO)) { + mxge_encap_tso(sc, m, cnt, ip_off); + return; + } + + req = tx->req_list; + cksum_offset = 0; + pseudo_hdr_offset = 0; + flags = MXGEFW_FLAGS_NO_TSO; + + /* checksum offloading? */ + if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { + /* ensure ip header is in first mbuf, copy + it to a scratch buffer if not */ + if (__predict_false(m->m_len < ip_off + sizeof (*ip))) { + m_copydata(m, 0, ip_off + sizeof (*ip), + sc->scratch); + ip = (struct ip *)(sc->scratch + ip_off); + } else { + ip = (struct ip *)(mtod(m, char *) + ip_off); + } + cksum_offset = ip_off + (ip->ip_hl << 2); + pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; + pseudo_hdr_offset = htobe16(pseudo_hdr_offset); + req->cksum_offset = cksum_offset; + flags |= MXGEFW_FLAGS_CKSUM; + odd_flag = MXGEFW_FLAGS_ALIGN_ODD; + } else { + odd_flag = 0; + } + if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE) + flags |= MXGEFW_FLAGS_SMALL; + + /* convert segments into a request list */ + cum_len = 0; + seg = tx->seg_list; + req->flags = MXGEFW_FLAGS_FIRST; + for (i = 0; i < cnt; i++) { + req->addr_low = + htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); + req->addr_high = + htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); + req->length = htobe16(seg->ds_len); + req->cksum_offset = cksum_offset; + if (cksum_offset > seg->ds_len) + cksum_offset -= seg->ds_len; + else + cksum_offset = 0; + req->pseudo_hdr_offset = pseudo_hdr_offset; + req->pad = 0; /* complete solid 16-byte block */ + req->rdma_count = 1; + req->flags |= flags | ((cum_len & 1) * odd_flag); + cum_len += seg->ds_len; + seg++; + req++; + req->flags = 0; + } + req--; + /* pad runts to 60 bytes */ + if (cum_len < 60) { + req++; + req->addr_low = + htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); + req->addr_high = + htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); + req->length = htobe16(60 - cum_len); + req->cksum_offset = 0; + req->pseudo_hdr_offset = pseudo_hdr_offset; + req->pad = 0; /* complete solid 16-byte block */ + req->rdma_count = 1; + req->flags |= flags | ((cum_len & 1) * odd_flag); + cnt++; + } + + tx->req_list[0].rdma_count = cnt; +#if 0 + /* print what the firmware will see */ + for (i = 0; i < cnt; i++) { + printf("%d: addr: 0x%x 0x%x len:%d pso%d," + "cso:%d, flags:0x%x, rdma:%d\n", + i, (int)ntohl(tx->req_list[i].addr_high), + (int)ntohl(tx->req_list[i].addr_low), + (int)ntohs(tx->req_list[i].length), + (int)ntohs(tx->req_list[i].pseudo_hdr_offset), + tx->req_list[i].cksum_offset, tx->req_list[i].flags, + tx->req_list[i].rdma_count); + } + printf("--------------\n"); +#endif + tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; + mxge_submit_req(tx, tx->req_list, cnt); + return; + +drop: + m_freem(m); + ifp->if_oerrors++; + return; +} + + + + +static inline void +mxge_start_locked(mxge_softc_t *sc) +{ + struct mbuf *m; + struct ifnet *ifp; + mxge_tx_buf_t *tx; + + ifp = sc->ifp; + tx = &sc->tx; + while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) { + return; + } + /* let BPF see it */ + BPF_MTAP(ifp, m); + + /* give it to the nic */ + mxge_encap(sc, m); + } + /* ran out of transmit slots */ + if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { + sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; + tx->stall++; + } +} + +static void +mxge_start(struct ifnet *ifp) +{ + mxge_softc_t *sc = ifp->if_softc; + + + mtx_lock(&sc->tx_mtx); + mxge_start_locked(sc); + mtx_unlock(&sc->tx_mtx); +} + +/* + * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy + * at most 32 bytes at a time, so as to avoid involving the software + * pio handler in the nic. We re-write the first segment's low + * DMA address to mark it valid only after we write the entire chunk + * in a burst + */ +static inline void +mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst, + mcp_kreq_ether_recv_t *src) +{ + uint32_t low; + + low = src->addr_low; + src->addr_low = 0xffffffff; + mxge_pio_copy(dst, src, 4 * sizeof (*src)); + mb(); + mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src)); + mb(); + src->addr_low = low; + dst->addr_low = low; + mb(); +} + +static int +mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx) +{ + bus_dma_segment_t seg; + struct mbuf *m; + mxge_rx_buf_t *rx = &sc->rx_small; + int cnt, err; + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) { + rx->alloc_fail++; + err = ENOBUFS; + goto done; + } + m->m_len = MHLEN; + err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, + &seg, &cnt, BUS_DMA_NOWAIT); + if (err != 0) { + m_free(m); + goto done; + } + rx->info[idx].m = m; + rx->shadow[idx].addr_low = + htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); + rx->shadow[idx].addr_high = + htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); + +done: + if ((idx & 7) == 7) + mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]); + return err; +} + +static int +mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx) +{ + bus_dma_segment_t seg[3]; + struct mbuf *m; + mxge_rx_buf_t *rx = &sc->rx_big; + int cnt, err, i; + + if (rx->cl_size == MCLBYTES) + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + else + m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size); + if (m == NULL) { + rx->alloc_fail++; + err = ENOBUFS; + goto done; + } + m->m_len = rx->cl_size; + err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, + seg, &cnt, BUS_DMA_NOWAIT); + if (err != 0) { + m_free(m); + goto done; + } + rx->info[idx].m = m; + + for (i = 0; i < cnt; i++) { + rx->shadow[idx + i].addr_low = + htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr)); + rx->shadow[idx + i].addr_high = + htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr)); + } + + +done: + for (i = 0; i < rx->nbufs; i++) { + if ((idx & 7) == 7) { + mxge_submit_8rx(&rx->lanai[idx - 7], + &rx->shadow[idx - 7]); + } + idx++; + } + return err; +} + +/* + * Myri10GE hardware checksums are not valid if the sender + * padded the frame with non-zero padding. This is because + * the firmware just does a simple 16-bit 1s complement + * checksum across the entire frame, excluding the first 14 + * bytes. It is best to simply to check the checksum and + * tell the stack about it only if the checksum is good + */ + +static inline uint16_t +mxge_rx_csum(struct mbuf *m, int csum) +{ + struct ether_header *eh; + struct ip *ip; + uint16_t c; + + eh = mtod(m, struct ether_header *); + + /* only deal with IPv4 TCP & UDP for now */ + if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP))) + return 1; + ip = (struct ip *)(eh + 1); + if (__predict_false(ip->ip_p != IPPROTO_TCP && + ip->ip_p != IPPROTO_UDP)) + return 1; + + c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htonl(ntohs(csum) + ntohs(ip->ip_len) + + - (ip->ip_hl << 2) + ip->ip_p)); + c ^= 0xffff; + return (c); +} + +static void +mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum) +{ + struct ether_vlan_header *evl; + struct ether_header *eh; + uint32_t partial; + + evl = mtod(m, struct ether_vlan_header *); + eh = mtod(m, struct ether_header *); + + /* + * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes + * after what the firmware thought was the end of the ethernet + * header. + */ + + /* put checksum into host byte order */ + *csum = ntohs(*csum); + partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN)); + (*csum) += ~partial; + (*csum) += ((*csum) < ~partial); + (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF); + (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF); + + /* restore checksum to network byte order; + later consumers expect this */ + *csum = htons(*csum); + + /* save the tag */ + m->m_flags |= M_VLANTAG; + m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); + + /* + * Remove the 802.1q header by copying the Ethernet + * addresses over it and adjusting the beginning of + * the data in the mbuf. The encapsulated Ethernet + * type field is already in place. + */ + bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN, + ETHER_HDR_LEN - ETHER_TYPE_LEN); + m_adj(m, ETHER_VLAN_ENCAP_LEN); +} + + +static inline void +mxge_rx_done_big(mxge_softc_t *sc, uint32_t len, uint32_t csum) +{ + struct ifnet *ifp; + struct mbuf *m; + struct ether_header *eh; + mxge_rx_buf_t *rx; + bus_dmamap_t old_map; + int idx; + uint16_t tcpudp_csum; + + ifp = sc->ifp; + rx = &sc->rx_big; + idx = rx->cnt & rx->mask; + rx->cnt += rx->nbufs; + /* save a pointer to the received mbuf */ + m = rx->info[idx].m; + /* try to replace the received mbuf */ + if (mxge_get_buf_big(sc, rx->extra_map, idx)) { + /* drop the frame -- the old mbuf is re-cycled */ + ifp->if_ierrors++; + return; + } + + /* unmap the received buffer */ + old_map = rx->info[idx].map; + bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(rx->dmat, old_map); + + /* swap the bus_dmamap_t's */ + rx->info[idx].map = rx->extra_map; + rx->extra_map = old_map; + + /* mcp implicitly skips 1st 2 bytes so that packet is properly + * aligned */ + m->m_data += MXGEFW_PAD; + + m->m_pkthdr.rcvif = ifp; + m->m_len = m->m_pkthdr.len = len; + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + if (eh->ether_type == htons(ETHERTYPE_VLAN)) { + mxge_vlan_tag_remove(m, &csum); + } + /* if the checksum is valid, mark it in the mbuf header */ + if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) { + if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum))) + return; + /* otherwise, it was a UDP frame, or a TCP frame which + we could not do LRO on. Tell the stack that the + checksum is good */ + m->m_pkthdr.csum_data = 0xffff; + m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID; + } + /* pass the frame up the stack */ + (*ifp->if_input)(ifp, m); +} + +static inline void +mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum) +{ + struct ifnet *ifp; + struct ether_header *eh; + struct mbuf *m; + mxge_rx_buf_t *rx; + bus_dmamap_t old_map; + int idx; + uint16_t tcpudp_csum; + + ifp = sc->ifp; + rx = &sc->rx_small; + idx = rx->cnt & rx->mask; + rx->cnt++; + /* save a pointer to the received mbuf */ + m = rx->info[idx].m; + /* try to replace the received mbuf */ + if (mxge_get_buf_small(sc, rx->extra_map, idx)) { + /* drop the frame -- the old mbuf is re-cycled */ + ifp->if_ierrors++; + return; + } + + /* unmap the received buffer */ + old_map = rx->info[idx].map; + bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(rx->dmat, old_map); + + /* swap the bus_dmamap_t's */ + rx->info[idx].map = rx->extra_map; + rx->extra_map = old_map; + + /* mcp implicitly skips 1st 2 bytes so that packet is properly + * aligned */ + m->m_data += MXGEFW_PAD; + + m->m_pkthdr.rcvif = ifp; + m->m_len = m->m_pkthdr.len = len; + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + if (eh->ether_type == htons(ETHERTYPE_VLAN)) { + mxge_vlan_tag_remove(m, &csum); + } + /* if the checksum is valid, mark it in the mbuf header */ + if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) { + if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum))) + return; + /* otherwise, it was a UDP frame, or a TCP frame which + we could not do LRO on. Tell the stack that the + checksum is good */ + m->m_pkthdr.csum_data = 0xffff; + m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID; + } + + /* pass the frame up the stack */ + (*ifp->if_input)(ifp, m); +} + +static inline void +mxge_clean_rx_done(mxge_softc_t *sc) +{ + mxge_rx_done_t *rx_done = &sc->rx_done; + struct lro_entry *lro; + int limit = 0; + uint16_t length; + uint16_t checksum; + + + while (rx_done->entry[rx_done->idx].length != 0) { + length = ntohs(rx_done->entry[rx_done->idx].length); + rx_done->entry[rx_done->idx].length = 0; + checksum = rx_done->entry[rx_done->idx].checksum; + if (length <= (MHLEN - MXGEFW_PAD)) + mxge_rx_done_small(sc, length, checksum); + else + mxge_rx_done_big(sc, length, checksum); + rx_done->cnt++; + rx_done->idx = rx_done->cnt & rx_done->mask; + + /* limit potential for livelock */ + if (__predict_false(++limit > 2 * rx_done->mask)) + break; + } + while(!SLIST_EMPTY(&sc->lro_active)) { + lro = SLIST_FIRST(&sc->lro_active); + SLIST_REMOVE_HEAD(&sc->lro_active, next); + mxge_lro_flush(sc, lro); + } +} + + +static inline void +mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx) +{ + struct ifnet *ifp; + mxge_tx_buf_t *tx; + struct mbuf *m; + bus_dmamap_t map; + int idx, limit; + + limit = 0; + tx = &sc->tx; + ifp = sc->ifp; + while (tx->pkt_done != mcp_idx) { + idx = tx->done & tx->mask; + tx->done++; + m = tx->info[idx].m; + /* mbuf and DMA map only attached to the first + segment per-mbuf */ + if (m != NULL) { + ifp->if_opackets++; + tx->info[idx].m = NULL; + map = tx->info[idx].map; + bus_dmamap_unload(tx->dmat, map); + m_freem(m); + } + if (tx->info[idx].flag) { + tx->info[idx].flag = 0; + tx->pkt_done++; + } + /* limit potential for livelock by only handling + 2 full tx rings per call */ + if (__predict_false(++limit > 2 * tx->mask)) + break; + } + + /* If we have space, clear IFF_OACTIVE to tell the stack that + its OK to send packets */ + + if (ifp->if_drv_flags & IFF_DRV_OACTIVE && + tx->req - tx->done < (tx->mask + 1)/4) { + mtx_lock(&sc->tx_mtx); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + sc->tx.wake++; + mxge_start_locked(sc); + mtx_unlock(&sc->tx_mtx); + } +} + +static void +mxge_intr(void *arg) +{ + mxge_softc_t *sc = arg; + mcp_irq_data_t *stats = sc->fw_stats; + mxge_tx_buf_t *tx = &sc->tx; + mxge_rx_done_t *rx_done = &sc->rx_done; + uint32_t send_done_count; + uint8_t valid; + + + /* make sure the DMA has finished */ + if (!stats->valid) { + return; + } + valid = stats->valid; + + if (!sc->msi_enabled) { + /* lower legacy IRQ */ + *sc->irq_deassert = 0; + if (!mxge_deassert_wait) + /* don't wait for conf. that irq is low */ + stats->valid = 0; + } else { + stats->valid = 0; + } + + /* loop while waiting for legacy irq deassertion */ + do { + /* check for transmit completes and receives */ + send_done_count = be32toh(stats->send_done_count); + while ((send_done_count != tx->pkt_done) || + (rx_done->entry[rx_done->idx].length != 0)) { + mxge_tx_done(sc, (int)send_done_count); + mxge_clean_rx_done(sc); + send_done_count = be32toh(stats->send_done_count); + } + } while (*((volatile uint8_t *) &stats->valid)); + + if (__predict_false(stats->stats_updated)) { + if (sc->link_state != stats->link_up) { + sc->link_state = stats->link_up; + if (sc->link_state) { + if_link_state_change(sc->ifp, LINK_STATE_UP); + if (mxge_verbose) + device_printf(sc->dev, "link up\n"); + } else { + if_link_state_change(sc->ifp, LINK_STATE_DOWN); + if (mxge_verbose) + device_printf(sc->dev, "link down\n"); + } + } + if (sc->rdma_tags_available != + be32toh(sc->fw_stats->rdma_tags_available)) { + sc->rdma_tags_available = + be32toh(sc->fw_stats->rdma_tags_available); + device_printf(sc->dev, "RDMA timed out! %d tags " + "left\n", sc->rdma_tags_available); + } + sc->down_cnt += stats->link_down; + } + + /* check to see if we have rx token to pass back */ + if (valid & 0x1) + *sc->irq_claim = be32toh(3); + *(sc->irq_claim + 1) = be32toh(3); +} + +static void +mxge_init(void *arg) +{ +} + + + +static void +mxge_free_mbufs(mxge_softc_t *sc) +{ + int i; + + for (i = 0; i <= sc->rx_big.mask; i++) { + if (sc->rx_big.info[i].m == NULL) + continue; + bus_dmamap_unload(sc->rx_big.dmat, + sc->rx_big.info[i].map); + m_freem(sc->rx_big.info[i].m); + sc->rx_big.info[i].m = NULL; + } + + for (i = 0; i <= sc->rx_small.mask; i++) { + if (sc->rx_small.info[i].m == NULL) + continue; + bus_dmamap_unload(sc->rx_small.dmat, + sc->rx_small.info[i].map); + m_freem(sc->rx_small.info[i].m); + sc->rx_small.info[i].m = NULL; + } + + for (i = 0; i <= sc->tx.mask; i++) { + sc->tx.info[i].flag = 0; + if (sc->tx.info[i].m == NULL) + continue; + bus_dmamap_unload(sc->tx.dmat, + sc->tx.info[i].map); + m_freem(sc->tx.info[i].m); + sc->tx.info[i].m = NULL; + } +} + +static void +mxge_free_rings(mxge_softc_t *sc) +{ + int i; + + if (sc->rx_done.entry != NULL) + mxge_dma_free(&sc->rx_done.dma); + sc->rx_done.entry = NULL; + if (sc->tx.req_bytes != NULL) + free(sc->tx.req_bytes, M_DEVBUF); + if (sc->tx.seg_list != NULL) + free(sc->tx.seg_list, M_DEVBUF); + if (sc->rx_small.shadow != NULL) + free(sc->rx_small.shadow, M_DEVBUF); + if (sc->rx_big.shadow != NULL) + free(sc->rx_big.shadow, M_DEVBUF); + if (sc->tx.info != NULL) { + if (sc->tx.dmat != NULL) { + for (i = 0; i <= sc->tx.mask; i++) { + bus_dmamap_destroy(sc->tx.dmat, + sc->tx.info[i].map); + } + bus_dma_tag_destroy(sc->tx.dmat); + } + free(sc->tx.info, M_DEVBUF); + } + if (sc->rx_small.info != NULL) { + if (sc->rx_small.dmat != NULL) { + for (i = 0; i <= sc->rx_small.mask; i++) { + bus_dmamap_destroy(sc->rx_small.dmat, + sc->rx_small.info[i].map); + } + bus_dmamap_destroy(sc->rx_small.dmat, + sc->rx_small.extra_map); + bus_dma_tag_destroy(sc->rx_small.dmat); + } + free(sc->rx_small.info, M_DEVBUF); + } + if (sc->rx_big.info != NULL) { + if (sc->rx_big.dmat != NULL) { + for (i = 0; i <= sc->rx_big.mask; i++) { + bus_dmamap_destroy(sc->rx_big.dmat, + sc->rx_big.info[i].map); + } + bus_dmamap_destroy(sc->rx_big.dmat, + sc->rx_big.extra_map); + bus_dma_tag_destroy(sc->rx_big.dmat); + } + free(sc->rx_big.info, M_DEVBUF); + } +} + +static int +mxge_alloc_rings(mxge_softc_t *sc) +{ + mxge_cmd_t cmd; + int tx_ring_size, rx_ring_size; + int tx_ring_entries, rx_ring_entries; + int i, err; + unsigned long bytes; + + /* get ring sizes */ + err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd); + tx_ring_size = cmd.data0; + err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); + if (err != 0) { + device_printf(sc->dev, "Cannot determine ring sizes\n"); + goto abort_with_nothing; + } + + rx_ring_size = cmd.data0; + + tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); + rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t); + IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1); + sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; + IFQ_SET_READY(&sc->ifp->if_snd); + + sc->tx.mask = tx_ring_entries - 1; + sc->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4); + sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1; + sc->rx_done.mask = (2 * rx_ring_entries) - 1; + + err = ENOMEM; + + /* allocate interrupt queues */ + bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry); + err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096); + if (err != 0) + goto abort_with_nothing; + sc->rx_done.entry = sc->rx_done.dma.addr; + bzero(sc->rx_done.entry, bytes); + + /* allocate the tx request copy block */ + bytes = 8 + + sizeof (*sc->tx.req_list) * (sc->tx.max_desc + 4); + sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK); + if (sc->tx.req_bytes == NULL) + goto abort_with_alloc; + /* ensure req_list entries are aligned to 8 bytes */ + sc->tx.req_list = (mcp_kreq_ether_send_t *) + ((unsigned long)(sc->tx.req_bytes + 7) & ~7UL); + + /* allocate the tx busdma segment list */ + bytes = sizeof (*sc->tx.seg_list) * sc->tx.max_desc; + sc->tx.seg_list = (bus_dma_segment_t *) + malloc(bytes, M_DEVBUF, M_WAITOK); + if (sc->tx.seg_list == NULL) + goto abort_with_alloc; + + /* allocate the rx shadow rings */ + bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow); + sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->rx_small.shadow == NULL) + goto abort_with_alloc; + + bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow); + sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->rx_big.shadow == NULL) + goto abort_with_alloc; + + /* allocate the host info rings */ + bytes = tx_ring_entries * sizeof (*sc->tx.info); + sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->tx.info == NULL) + goto abort_with_alloc; + + bytes = rx_ring_entries * sizeof (*sc->rx_small.info); + sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->rx_small.info == NULL) + goto abort_with_alloc; + + bytes = rx_ring_entries * sizeof (*sc->rx_big.info); + sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->rx_big.info == NULL) + goto abort_with_alloc; + + /* allocate the busdma resources */ + err = bus_dma_tag_create(sc->parent_dmat, /* parent */ + 1, /* alignment */ + sc->tx.boundary, /* boundary */ + BUS_SPACE_MAXADDR, /* low */ + BUS_SPACE_MAXADDR, /* high */ + NULL, NULL, /* filter */ + 65536 + 256, /* maxsize */ + sc->tx.max_desc - 2, /* num segs */ + sc->tx.boundary, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lock */ + &sc->tx.dmat); /* tag */ + + if (err != 0) { + device_printf(sc->dev, "Err %d allocating tx dmat\n", + err); + goto abort_with_alloc; + } + + err = bus_dma_tag_create(sc->parent_dmat, /* parent */ + 1, /* alignment */ + 4096, /* boundary */ + BUS_SPACE_MAXADDR, /* low */ + BUS_SPACE_MAXADDR, /* high */ + NULL, NULL, /* filter */ + MHLEN, /* maxsize */ + 1, /* num segs */ + MHLEN, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lock */ + &sc->rx_small.dmat); /* tag */ + if (err != 0) { + device_printf(sc->dev, "Err %d allocating rx_small dmat\n", + err); + goto abort_with_alloc; + } + + err = bus_dma_tag_create(sc->parent_dmat, /* parent */ + 1, /* alignment */ + 4096, /* boundary */ + BUS_SPACE_MAXADDR, /* low */ + BUS_SPACE_MAXADDR, /* high */ + NULL, NULL, /* filter */ + 3*4096, /* maxsize */ + 3, /* num segs */ + 4096, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lock */ + &sc->rx_big.dmat); /* tag */ + if (err != 0) { + device_printf(sc->dev, "Err %d allocating rx_big dmat\n", + err); + goto abort_with_alloc; + } + + /* now use these tags to setup dmamaps for each slot + in each ring */ + for (i = 0; i <= sc->tx.mask; i++) { + err = bus_dmamap_create(sc->tx.dmat, 0, + &sc->tx.info[i].map); + if (err != 0) { + device_printf(sc->dev, "Err %d tx dmamap\n", + err); + goto abort_with_alloc; + } + } + for (i = 0; i <= sc->rx_small.mask; i++) { + err = bus_dmamap_create(sc->rx_small.dmat, 0, + &sc->rx_small.info[i].map); + if (err != 0) { + device_printf(sc->dev, "Err %d rx_small dmamap\n", + err); + goto abort_with_alloc; + } + } + err = bus_dmamap_create(sc->rx_small.dmat, 0, + &sc->rx_small.extra_map); + if (err != 0) { + device_printf(sc->dev, "Err %d extra rx_small dmamap\n", + err); + goto abort_with_alloc; + } + + for (i = 0; i <= sc->rx_big.mask; i++) { + err = bus_dmamap_create(sc->rx_big.dmat, 0, + &sc->rx_big.info[i].map); + if (err != 0) { + device_printf(sc->dev, "Err %d rx_big dmamap\n", + err); + goto abort_with_alloc; + } + } + err = bus_dmamap_create(sc->rx_big.dmat, 0, + &sc->rx_big.extra_map); + if (err != 0) { + device_printf(sc->dev, "Err %d extra rx_big dmamap\n", + err); + goto abort_with_alloc; + } + return 0; + +abort_with_alloc: + mxge_free_rings(sc); + +abort_with_nothing: + return err; +} + +static void +mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs) +{ + int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD; + + if (bufsize < MCLBYTES) { + /* easy, everything fits in a single buffer */ + *big_buf_size = MCLBYTES; + *cl_size = MCLBYTES; + *nbufs = 1; + return; + } + + if (bufsize < MJUMPAGESIZE) { + /* still easy, everything still fits in a single buffer */ + *big_buf_size = MJUMPAGESIZE; + *cl_size = MJUMPAGESIZE; + *nbufs = 1; + return; + } + /* now we need to use virtually contiguous buffers */ + *cl_size = MJUM9BYTES; + *big_buf_size = 4096; + *nbufs = mtu / 4096 + 1; + /* needs to be a power of two, so round up */ + if (*nbufs == 3) + *nbufs = 4; +} + +static int +mxge_open(mxge_softc_t *sc) +{ + mxge_cmd_t cmd; + int i, err, big_bytes; + bus_dmamap_t map; + bus_addr_t bus; + struct lro_entry *lro_entry; + + SLIST_INIT(&sc->lro_free); + SLIST_INIT(&sc->lro_active); + + for (i = 0; i < sc->lro_cnt; i++) { + lro_entry = (struct lro_entry *) + malloc(sizeof (*lro_entry), M_DEVBUF, M_NOWAIT | M_ZERO); + if (lro_entry == NULL) { + sc->lro_cnt = i; + break; + } + SLIST_INSERT_HEAD(&sc->lro_free, lro_entry, next); + } + + /* Copy the MAC address in case it was overridden */ + bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN); + + err = mxge_reset(sc, 1); + if (err != 0) { + device_printf(sc->dev, "failed to reset\n"); + return EIO; + } + + mxge_choose_params(sc->ifp->if_mtu, &big_bytes, + &sc->rx_big.cl_size, &sc->rx_big.nbufs); + + cmd.data0 = sc->rx_big.nbufs; + err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, + &cmd); + /* error is only meaningful if we're trying to set + MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */ + if (err && sc->rx_big.nbufs > 1) { + device_printf(sc->dev, + "Failed to set alway-use-n to %d\n", + sc->rx_big.nbufs); + return EIO; + } + /* get the lanai pointers to the send and receive rings */ + + err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd); + sc->tx.lanai = + (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); + err |= mxge_send_cmd(sc, + MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd); + sc->rx_small.lanai = + (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); + err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd); + sc->rx_big.lanai = + (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); + + if (err != 0) { + device_printf(sc->dev, + "failed to get ring sizes or locations\n"); + return EIO; + } + + /* stock receive rings */ + for (i = 0; i <= sc->rx_small.mask; i++) { + map = sc->rx_small.info[i].map; + err = mxge_get_buf_small(sc, map, i); + if (err) { + device_printf(sc->dev, "alloced %d/%d smalls\n", + i, sc->rx_small.mask + 1); + goto abort; + } + } + for (i = 0; i <= sc->rx_big.mask; i++) { + sc->rx_big.shadow[i].addr_low = 0xffffffff; + sc->rx_big.shadow[i].addr_high = 0xffffffff; + } + for (i = 0; i <= sc->rx_big.mask; i += sc->rx_big.nbufs) { + map = sc->rx_big.info[i].map; + err = mxge_get_buf_big(sc, map, i); + if (err) { + device_printf(sc->dev, "alloced %d/%d bigs\n", + i, sc->rx_big.mask + 1); + goto abort; + } + } + + /* Give the firmware the mtu and the big and small buffer + sizes. The firmware wants the big buf size to be a power + of two. Luckily, FreeBSD's clusters are powers of two */ + cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd); + cmd.data0 = MHLEN - MXGEFW_PAD; + err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, + &cmd); + cmd.data0 = big_bytes; + err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd); + + if (err != 0) { + device_printf(sc->dev, "failed to setup params\n"); + goto abort; + } + + /* Now give him the pointer to the stats block */ + cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr); + cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr); + cmd.data2 = sizeof(struct mcp_irq_data); + err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd); + + if (err != 0) { + bus = sc->fw_stats_dma.bus_addr; + bus += offsetof(struct mcp_irq_data, send_done_count); + cmd.data0 = MXGE_LOWPART_TO_U32(bus); + cmd.data1 = MXGE_HIGHPART_TO_U32(bus); + err = mxge_send_cmd(sc, + MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, + &cmd); + /* Firmware cannot support multicast without STATS_DMA_V2 */ + sc->fw_multicast_support = 0; + } else { + sc->fw_multicast_support = 1; + } + + if (err != 0) { + device_printf(sc->dev, "failed to setup params\n"); + goto abort; + } + + /* Finally, start the firmware running */ + err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd); + if (err) { + device_printf(sc->dev, "Couldn't bring up link\n"); + goto abort; + } + sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; + sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + return 0; + + +abort: + mxge_free_mbufs(sc); + + return err; +} + +static int +mxge_close(mxge_softc_t *sc) +{ + struct lro_entry *lro_entry; + mxge_cmd_t cmd; + int err, old_down_cnt; + + sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + old_down_cnt = sc->down_cnt; + mb(); + err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd); + if (err) { + device_printf(sc->dev, "Couldn't bring down link\n"); + } + if (old_down_cnt == sc->down_cnt) { + /* wait for down irq */ + DELAY(10 * sc->intr_coal_delay); + } + if (old_down_cnt == sc->down_cnt) { + device_printf(sc->dev, "never got down irq\n"); + } + + mxge_free_mbufs(sc); + + while (!SLIST_EMPTY(&sc->lro_free)) { + lro_entry = SLIST_FIRST(&sc->lro_free); + SLIST_REMOVE_HEAD(&sc->lro_free, next); + } + return 0; +} + +static void +mxge_setup_cfg_space(mxge_softc_t *sc) +{ + device_t dev = sc->dev; + int reg; + uint16_t cmd, lnk, pectl; + + /* find the PCIe link width and set max read request to 4KB*/ + if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { + lnk = pci_read_config(dev, reg + 0x12, 2); + sc->link_width = (lnk >> 4) & 0x3f; + + pectl = pci_read_config(dev, reg + 0x8, 2); + pectl = (pectl & ~0x7000) | (5 << 12); + pci_write_config(dev, reg + 0x8, pectl, 2); + } + + /* Enable DMA and Memory space access */ + pci_enable_busmaster(dev); + cmd = pci_read_config(dev, PCIR_COMMAND, 2); + cmd |= PCIM_CMD_MEMEN; + pci_write_config(dev, PCIR_COMMAND, cmd, 2); +} + +static uint32_t +mxge_read_reboot(mxge_softc_t *sc) +{ + device_t dev = sc->dev; + uint32_t vs; + + /* find the vendor specific offset */ + if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) { + device_printf(sc->dev, + "could not find vendor specific offset\n"); + return (uint32_t)-1; + } + /* enable read32 mode */ + pci_write_config(dev, vs + 0x10, 0x3, 1); + /* tell NIC which register to read */ + pci_write_config(dev, vs + 0x18, 0xfffffff0, 4); + return (pci_read_config(dev, vs + 0x14, 4)); +} + +static void +mxge_watchdog_reset(mxge_softc_t *sc) +{ + int err; + uint32_t reboot; + uint16_t cmd; + + err = ENXIO; + + device_printf(sc->dev, "Watchdog reset!\n"); + + /* + * check to see if the NIC rebooted. If it did, then all of + * PCI config space has been reset, and things like the + * busmaster bit will be zero. If this is the case, then we + * must restore PCI config space before the NIC can be used + * again + */ + cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); + if (cmd == 0xffff) { + /* + * maybe the watchdog caught the NIC rebooting; wait + * up to 100ms for it to finish. If it does not come + * back, then give up + */ + DELAY(1000*100); + cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); + if (cmd == 0xffff) { + device_printf(sc->dev, "NIC disappeared!\n"); + goto abort; + } + } + if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { + /* print the reboot status */ + reboot = mxge_read_reboot(sc); + device_printf(sc->dev, "NIC rebooted, status = 0x%x\n", + reboot); + /* restore PCI configuration space */ + + /* XXXX waiting for pci_cfg_restore() to be exported */ + goto abort; /* just abort for now */ + + /* and redo any changes we made to our config space */ + mxge_setup_cfg_space(sc); + } else { + device_printf(sc->dev, "NIC did not reboot, ring state:\n"); + device_printf(sc->dev, "tx.req=%d tx.done=%d\n", + sc->tx.req, sc->tx.done); + device_printf(sc->dev, "pkt_done=%d fw=%d\n", + sc->tx.pkt_done, + be32toh(sc->fw_stats->send_done_count)); + } + + if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { + mxge_close(sc); + err = mxge_open(sc); + } + +abort: + /* + * stop the watchdog if the nic is dead, to avoid spamming the + * console + */ + if (err != 0) { + callout_stop(&sc->co_hdl); + } +} + +static void +mxge_watchdog(mxge_softc_t *sc) +{ + mxge_tx_buf_t *tx = &sc->tx; + + /* see if we have outstanding transmits, which + have been pending for more than mxge_ticks */ + if (tx->req != tx->done && + tx->watchdog_req != tx->watchdog_done && + tx->done == tx->watchdog_done) + mxge_watchdog_reset(sc); + + tx->watchdog_req = tx->req; + tx->watchdog_done = tx->done; +} + +static void +mxge_tick(void *arg) +{ + mxge_softc_t *sc = arg; + + + /* Synchronize with possible callout reset/stop. */ + if (callout_pending(&sc->co_hdl) || + !callout_active(&sc->co_hdl)) { + mtx_unlock(&sc->driver_mtx); + return; + } + + callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); + mxge_watchdog(sc); +} + +static int +mxge_media_change(struct ifnet *ifp) +{ + return EINVAL; +} + +static int +mxge_change_mtu(mxge_softc_t *sc, int mtu) +{ + struct ifnet *ifp = sc->ifp; + int real_mtu, old_mtu; + int err = 0; + + + real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + if ((real_mtu > sc->max_mtu) || real_mtu < 60) + return EINVAL; + mtx_lock(&sc->driver_mtx); + old_mtu = ifp->if_mtu; + ifp->if_mtu = mtu; + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + callout_stop(&sc->co_hdl); + mxge_close(sc); + err = mxge_open(sc); + if (err != 0) { + ifp->if_mtu = old_mtu; + mxge_close(sc); + (void) mxge_open(sc); + } + callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); + } + mtx_unlock(&sc->driver_mtx); + return err; +} + +static void +mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + mxge_softc_t *sc = ifp->if_softc; + + + if (sc == NULL) + return; + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0; + ifmr->ifm_active = IFM_AUTO | IFM_ETHER; + ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0; +} + +static int +mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + mxge_softc_t *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int err, mask; + + err = 0; + switch (command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + err = ether_ioctl(ifp, command, data); + break; + + case SIOCSIFMTU: + err = mxge_change_mtu(sc, ifr->ifr_mtu); + break; + + case SIOCSIFFLAGS: + mtx_lock(&sc->driver_mtx); + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + err = mxge_open(sc); + callout_reset(&sc->co_hdl, mxge_ticks, + mxge_tick, sc); + } else { + /* take care of promis can allmulti + flag chages */ + mxge_change_promisc(sc, + ifp->if_flags & IFF_PROMISC); + mxge_set_multicast_list(sc); + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + mxge_close(sc); + callout_stop(&sc->co_hdl); + } + } + mtx_unlock(&sc->driver_mtx); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + mtx_lock(&sc->driver_mtx); + mxge_set_multicast_list(sc); + mtx_unlock(&sc->driver_mtx); + break; + + case SIOCSIFCAP: + mtx_lock(&sc->driver_mtx); + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if (mask & IFCAP_TXCSUM) { + if (IFCAP_TXCSUM & ifp->if_capenable) { + ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4); + ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP + | CSUM_TSO); + } else { + ifp->if_capenable |= IFCAP_TXCSUM; + ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); + } + } else if (mask & IFCAP_RXCSUM) { + if (IFCAP_RXCSUM & ifp->if_capenable) { + ifp->if_capenable &= ~IFCAP_RXCSUM; + sc->csum_flag = 0; + } else { + ifp->if_capenable |= IFCAP_RXCSUM; + sc->csum_flag = 1; + } + } + if (mask & IFCAP_TSO4) { + if (IFCAP_TSO4 & ifp->if_capenable) { + ifp->if_capenable &= ~IFCAP_TSO4; + ifp->if_hwassist &= ~CSUM_TSO; + } else if (IFCAP_TXCSUM & ifp->if_capenable) { + ifp->if_capenable |= IFCAP_TSO4; + ifp->if_hwassist |= CSUM_TSO; + } else { + printf("mxge requires tx checksum offload" + " be enabled to use TSO\n"); + err = EINVAL; + } + } + + if (mask & IFCAP_VLAN_HWTAGGING) + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + mtx_unlock(&sc->driver_mtx); + VLAN_CAPABILITIES(ifp); + + break; + + case SIOCGIFMEDIA: + err = ifmedia_ioctl(ifp, (struct ifreq *)data, + &sc->media, command); + break; + + default: + err = ENOTTY; + } + return err; +} + +static void +mxge_fetch_tunables(mxge_softc_t *sc) +{ + + TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled", + &mxge_flow_control); + TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay", + &mxge_intr_coal_delay); + TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable", + &mxge_nvidia_ecrc_enable); + TUNABLE_INT_FETCH("hw.mxge.force_firmware", + &mxge_force_firmware); + TUNABLE_INT_FETCH("hw.mxge.deassert_wait", + &mxge_deassert_wait); + TUNABLE_INT_FETCH("hw.mxge.verbose", + &mxge_verbose); + TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks); + TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt); + + if (bootverbose) + mxge_verbose = 1; + if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) + mxge_intr_coal_delay = 30; + if (mxge_ticks == 0) + mxge_ticks = hz; + sc->pause = mxge_flow_control; + +} + +static int +mxge_attach(device_t dev) +{ + mxge_softc_t *sc = device_get_softc(dev); + struct ifnet *ifp; + int count, rid, err; + + sc->dev = dev; + mxge_fetch_tunables(sc); + + err = bus_dma_tag_create(NULL, /* parent */ + 1, /* alignment */ + 4096, /* boundary */ + BUS_SPACE_MAXADDR, /* low */ + BUS_SPACE_MAXADDR, /* high */ + NULL, NULL, /* filter */ + 65536 + 256, /* maxsize */ + MXGE_MAX_SEND_DESC, /* num segs */ + 4096, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lock */ + &sc->parent_dmat); /* tag */ + + if (err != 0) { + device_printf(sc->dev, "Err %d allocating parent dmat\n", + err); + goto abort_with_nothing; + } + + ifp = sc->ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "can not if_alloc()\n"); + err = ENOSPC; + goto abort_with_parent_dmat; + } + snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd", + device_get_nameunit(dev)); + mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF); + snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx", + device_get_nameunit(dev)); + mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF); + snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name), + "%s:drv", device_get_nameunit(dev)); + mtx_init(&sc->driver_mtx, sc->driver_mtx_name, + MTX_NETWORK_LOCK, MTX_DEF); + + callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0); + + mxge_setup_cfg_space(sc); + + /* Map the board into the kernel */ + rid = PCIR_BARS; + sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, + ~0, 1, RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "could not map memory\n"); + err = ENXIO; + goto abort_with_lock; + } + sc->sram = rman_get_virtual(sc->mem_res); + sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; + if (sc->sram_size > rman_get_size(sc->mem_res)) { + device_printf(dev, "impossible memory region size %ld\n", + rman_get_size(sc->mem_res)); + err = ENXIO; + goto abort_with_mem_res; + } + + /* make NULL terminated copy of the EEPROM strings section of + lanai SRAM */ + bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE); + bus_space_read_region_1(rman_get_bustag(sc->mem_res), + rman_get_bushandle(sc->mem_res), + sc->sram_size - MXGE_EEPROM_STRINGS_SIZE, + sc->eeprom_strings, + MXGE_EEPROM_STRINGS_SIZE - 2); + err = mxge_parse_strings(sc); + if (err != 0) + goto abort_with_mem_res; + + /* Enable write combining for efficient use of PCIe bus */ + mxge_enable_wc(sc); + + /* Allocate the out of band dma memory */ + err = mxge_dma_alloc(sc, &sc->cmd_dma, + sizeof (mxge_cmd_t), 64); + if (err != 0) + goto abort_with_mem_res; + sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; + err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64); + if (err != 0) + goto abort_with_cmd_dma; + + err = mxge_dma_alloc(sc, &sc->fw_stats_dma, + sizeof (*sc->fw_stats), 64); + if (err != 0) + goto abort_with_zeropad_dma; + sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr; + + err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096); + if (err != 0) + goto abort_with_fw_stats; + + /* Add our ithread */ + count = pci_msi_count(dev); + if (count == 1 && pci_alloc_msi(dev, &count) == 0) { + rid = 1; + sc->msi_enabled = 1; + } else { + rid = 0; + } + sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, + 1, RF_SHAREABLE | RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "could not alloc interrupt\n"); + goto abort_with_dmabench; + } + if (mxge_verbose) + device_printf(dev, "using %s irq %ld\n", + sc->msi_enabled ? "MSI" : "INTx", + rman_get_start(sc->irq_res)); + /* select & load the firmware */ + err = mxge_select_firmware(sc); + if (err != 0) + goto abort_with_irq_res; + sc->intr_coal_delay = mxge_intr_coal_delay; + err = mxge_reset(sc, 0); + if (err != 0) + goto abort_with_irq_res; + + err = mxge_alloc_rings(sc); + if (err != 0) { + device_printf(sc->dev, "failed to allocate rings\n"); + goto abort_with_irq_res; + } + + err = bus_setup_intr(sc->dev, sc->irq_res, + INTR_TYPE_NET | INTR_MPSAFE, + NULL, mxge_intr, sc, &sc->ih); + if (err != 0) { + goto abort_with_rings; + } + /* hook into the network stack */ + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_baudrate = 100000000; + ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 | + IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM; + + sc->max_mtu = mxge_max_mtu(sc); + if (sc->max_mtu >= 9000) + ifp->if_capabilities |= IFCAP_JUMBO_MTU; + else + device_printf(dev, "MTU limited to %d. Install " + "latest firmware for 9000 byte jumbo support\n", + sc->max_mtu - ETHER_HDR_LEN); + ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO; + ifp->if_capenable = ifp->if_capabilities; + sc->csum_flag = 1; + ifp->if_init = mxge_init; + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = mxge_ioctl; + ifp->if_start = mxge_start; + ether_ifattach(ifp, sc->mac_addr); + /* ether_ifattach sets mtu to 1500 */ + if (ifp->if_capabilities & IFCAP_JUMBO_MTU) + ifp->if_mtu = 9000; + + /* Initialise the ifmedia structure */ + ifmedia_init(&sc->media, 0, mxge_media_change, + mxge_media_status); + ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); + mxge_add_sysctls(sc); + return 0; + +abort_with_rings: + mxge_free_rings(sc); +abort_with_irq_res: + bus_release_resource(dev, SYS_RES_IRQ, + sc->msi_enabled ? 1 : 0, sc->irq_res); + if (sc->msi_enabled) + pci_release_msi(dev); +abort_with_dmabench: + mxge_dma_free(&sc->dmabench_dma); +abort_with_fw_stats: + mxge_dma_free(&sc->fw_stats_dma); +abort_with_zeropad_dma: + mxge_dma_free(&sc->zeropad_dma); +abort_with_cmd_dma: + mxge_dma_free(&sc->cmd_dma); +abort_with_mem_res: + bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); +abort_with_lock: + pci_disable_busmaster(dev); + mtx_destroy(&sc->cmd_mtx); + mtx_destroy(&sc->tx_mtx); + mtx_destroy(&sc->driver_mtx); + if_free(ifp); +abort_with_parent_dmat: + bus_dma_tag_destroy(sc->parent_dmat); + +abort_with_nothing: + return err; +} + +static int +mxge_detach(device_t dev) +{ + mxge_softc_t *sc = device_get_softc(dev); + + if (sc->ifp->if_vlantrunk != NULL) { + device_printf(sc->dev, + "Detach vlans before removing module\n"); + return EBUSY; + } + mtx_lock(&sc->driver_mtx); + if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) + mxge_close(sc); + callout_stop(&sc->co_hdl); + mtx_unlock(&sc->driver_mtx); + ether_ifdetach(sc->ifp); + ifmedia_removeall(&sc->media); + mxge_dummy_rdma(sc, 0); + bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); + mxge_free_rings(sc); + bus_release_resource(dev, SYS_RES_IRQ, + sc->msi_enabled ? 1 : 0, sc->irq_res); + if (sc->msi_enabled) + pci_release_msi(dev); + + sc->rx_done.entry = NULL; + mxge_dma_free(&sc->rx_done.dma); + mxge_dma_free(&sc->fw_stats_dma); + mxge_dma_free(&sc->dmabench_dma); + mxge_dma_free(&sc->zeropad_dma); + mxge_dma_free(&sc->cmd_dma); + bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); + pci_disable_busmaster(dev); + mtx_destroy(&sc->cmd_mtx); + mtx_destroy(&sc->tx_mtx); + mtx_destroy(&sc->driver_mtx); + if_free(sc->ifp); + bus_dma_tag_destroy(sc->parent_dmat); + return 0; +} + +static int +mxge_shutdown(device_t dev) +{ + return 0; +} + +/* + This file uses Myri10GE driver indentation. + + Local Variables: + c-file-style:"linux" + tab-width:8 + End: +*/ Property changes on: stable/6/sys/dev/mxge/if_mxge.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/mxge/if_mxge_var.h =================================================================== --- stable/6/sys/dev/mxge/if_mxge_var.h (nonexistent) +++ stable/6/sys/dev/mxge/if_mxge_var.h (revision 170009) @@ -0,0 +1,263 @@ +/******************************************************************************* + +Copyright (c) 2006, Myricom Inc. +All rights reserved. + +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. Neither the name of the Myricom Inc, nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$FreeBSD$ + +***************************************************************************/ + +#define MXGE_ETH_STOPPED 0 +#define MXGE_ETH_STOPPING 1 +#define MXGE_ETH_STARTING 2 +#define MXGE_ETH_RUNNING 3 +#define MXGE_ETH_OPEN_FAILED 4 + +#define MXGE_FW_OFFSET 1024*1024 +#define MXGE_EEPROM_STRINGS_SIZE 256 +#define MXGE_MAX_SEND_DESC 128 + +typedef struct { + void *addr; + bus_addr_t bus_addr; + bus_dma_tag_t dmat; + bus_dmamap_t map; +} mxge_dma_t; + + +typedef struct { + mcp_slot_t *entry; + mxge_dma_t dma; + int cnt; + int idx; + int mask; +} mxge_rx_done_t; + +typedef struct +{ + uint32_t data0; + uint32_t data1; + uint32_t data2; +} mxge_cmd_t; + +struct mxge_rx_buffer_state { + struct mbuf *m; + bus_dmamap_t map; +}; + +struct mxge_tx_buffer_state { + struct mbuf *m; + bus_dmamap_t map; + int flag; +}; + +typedef struct +{ + volatile mcp_kreq_ether_recv_t *lanai; /* lanai ptr for recv ring */ + mcp_kreq_ether_recv_t *shadow; /* host shadow of recv ring */ + struct mxge_rx_buffer_state *info; + bus_dma_tag_t dmat; + bus_dmamap_t extra_map; + int cnt; + int nbufs; + int cl_size; + int alloc_fail; + int mask; /* number of rx slots -1 */ +} mxge_rx_buf_t; + +typedef struct +{ + volatile mcp_kreq_ether_send_t *lanai; /* lanai ptr for sendq */ + mcp_kreq_ether_send_t *req_list; /* host shadow of sendq */ + char *req_bytes; + bus_dma_segment_t *seg_list; + struct mxge_tx_buffer_state *info; + bus_dma_tag_t dmat; + int req; /* transmits submitted */ + int mask; /* number of transmit slots -1 */ + int done; /* transmits completed */ + int pkt_done; /* packets completed */ + int boundary; /* boundary transmits cannot cross*/ + int max_desc; /* max descriptors per xmit */ + int stall; /* #times hw queue exhausted */ + int wake; /* #times irq re-enabled xmit */ + int watchdog_req; /* cache of req */ + int watchdog_done; /* cache of done */ +} mxge_tx_buf_t; + +struct lro_entry; +struct lro_entry +{ + SLIST_ENTRY(lro_entry) next; + struct mbuf *m_head; + struct mbuf *m_tail; + int timestamp; + struct ip *ip; + uint32_t tsval; + uint32_t tsecr; + uint32_t source_ip; + uint32_t dest_ip; + uint32_t next_seq; + uint32_t ack_seq; + uint32_t len; + uint32_t data_csum; + uint16_t window; + uint16_t source_port; + uint16_t dest_port; + uint16_t append_cnt; + uint16_t mss; + +}; +SLIST_HEAD(lro_head, lro_entry); + +typedef struct { + struct ifnet* ifp; + struct mtx tx_mtx; + int csum_flag; /* rx_csums? */ + mxge_tx_buf_t tx; /* transmit ring */ + mxge_rx_buf_t rx_small; + mxge_rx_buf_t rx_big; + mxge_rx_done_t rx_done; + mcp_irq_data_t *fw_stats; + bus_dma_tag_t parent_dmat; + volatile uint8_t *sram; + struct lro_head lro_active; + struct lro_head lro_free; + int lro_queued; + int lro_flushed; + int lro_bad_csum; + int lro_cnt; + int sram_size; + volatile uint32_t *irq_deassert; + volatile uint32_t *irq_claim; + mcp_cmd_response_t *cmd; + mxge_dma_t cmd_dma; + mxge_dma_t zeropad_dma; + mxge_dma_t fw_stats_dma; + struct pci_dev *pdev; + int msi_enabled; + int link_state; + unsigned int rdma_tags_available; + int intr_coal_delay; + volatile uint32_t *intr_coal_delay_ptr; + int wc; + struct mtx cmd_mtx; + struct mtx driver_mtx; + int wake_queue; + int stop_queue; + int down_cnt; + int watchdog_resets; + int tx_defragged; + int pause; + struct resource *mem_res; + struct resource *irq_res; + void *ih; + char *fw_name; + char eeprom_strings[MXGE_EEPROM_STRINGS_SIZE]; + char fw_version[128]; + int fw_ver_major; + int fw_ver_minor; + int fw_ver_tiny; + int adopted_rx_filter_bug; + device_t dev; + struct ifmedia media; + int read_dma; + int write_dma; + int read_write_dma; + int fw_multicast_support; + int link_width; + int max_mtu; + int tx_defrag; + mxge_dma_t dmabench_dma; + struct callout co_hdl; + char *mac_addr_string; + uint8_t mac_addr[6]; /* eeprom mac address */ + char product_code_string[64]; + char serial_number_string[64]; + char scratch[256]; + char tx_mtx_name[16]; + char cmd_mtx_name[16]; + char driver_mtx_name[16]; +} mxge_softc_t; + +#define MXGE_PCI_VENDOR_MYRICOM 0x14c1 +#define MXGE_PCI_DEVICE_Z8E 0x0008 + +#define MXGE_HIGHPART_TO_U32(X) \ +(sizeof (X) == 8) ? ((uint32_t)((uint64_t)(X) >> 32)) : (0) +#define MXGE_LOWPART_TO_U32(X) ((uint32_t)(X)) + + +/* implement our own memory barriers, since bus_space_barrier + cannot handle write-combining regions */ + +#if defined (__GNUC__) + #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ + #define mb() __asm__ __volatile__ ("sfence;": : :"memory") + #elif #cpu(sparc64) || defined sparc64 || defined __sparcv9 + #define mb() __asm__ __volatile__ ("membar #MemIssue": : :"memory") + #elif #cpu(sparc) || defined sparc || defined __sparc__ + #define mb() __asm__ __volatile__ ("stbar;": : :"memory") + #else + #define mb() /* XXX just to make this compile */ + #endif +#else + #error "unknown compiler" +#endif + +static inline void +mxge_pio_copy(volatile void *to_v, void *from_v, size_t size) +{ + register volatile uintptr_t *to; + volatile uintptr_t *from; + size_t i; + + to = (volatile uintptr_t *) to_v; + from = from_v; + for (i = (size / sizeof (uintptr_t)); i; i--) { + *to = *from; + to++; + from++; + } + +} + +void mxge_lro_flush(mxge_softc_t *mgp, struct lro_entry *lro); +int mxge_lro_rx(mxge_softc_t *mgp, struct mbuf *m_head, uint32_t csum); + + + +/* + This file uses Myri10GE driver indentation. + + Local Variables: + c-file-style:"linux" + tab-width:8 + End: +*/ Property changes on: stable/6/sys/dev/mxge/if_mxge_var.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/mxge/mxge_lro.c =================================================================== --- stable/6/sys/dev/mxge/mxge_lro.c (nonexistent) +++ stable/6/sys/dev/mxge/mxge_lro.c (revision 170009) @@ -0,0 +1,351 @@ +/****************************************************************************** + +Copyright (c) 2007, Myricom Inc. +All rights reserved. + +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. Neither the name of the Myricom Inc, nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +***************************************************************************/ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + + +/* Assume len is a multiple of 4 */ +static uint16_t +mxge_csum_generic(uint16_t *raw, int len) +{ + uint32_t csum; + csum = 0; + while (len > 0) { + csum += *raw; + raw++; + csum += *raw; + raw++; + len -= 4; + } + csum = (csum >> 16) + (csum & 0xffff); + csum = (csum >> 16) + (csum & 0xffff); + return (uint16_t)csum; +} + + +void +mxge_lro_flush(mxge_softc_t *mgp, struct lro_entry *lro) +{ + struct ifnet *ifp; + struct ip *ip; + struct tcphdr *tcp; + uint32_t *ts_ptr; + uint32_t tcplen, tcp_csum; + + if (lro->append_cnt) { + /* incorporate the new len into the ip header and + * re-calculate the checksum */ + ip = lro->ip; + ip->ip_len = htons(lro->len - ETHER_HDR_LEN); + ip->ip_sum = 0; + ip->ip_sum = 0xffff ^ + mxge_csum_generic((uint16_t*)ip, + sizeof (*ip)); + + lro->m_head->m_pkthdr.csum_flags = CSUM_IP_CHECKED | + CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + lro->m_head->m_pkthdr.csum_data = 0xffff; + lro->m_head->m_pkthdr.len = lro->len; + + /* incorporate the latest ack into the tcp header */ + tcp = (struct tcphdr *) (ip + 1); + tcp->th_ack = lro->ack_seq; + tcp->th_win = lro->window; + /* incorporate latest timestamp into the tcp header */ + if (lro->timestamp) { + ts_ptr = (uint32_t *)(tcp + 1); + ts_ptr[1] = htonl(lro->tsval); + ts_ptr[2] = lro->tsecr; + } + /* + * update checksum in tcp header by re-calculating the + * tcp pseudoheader checksum, and adding it to the checksum + * of the tcp payload data + */ + tcp->th_sum = 0; + tcplen = lro->len - sizeof(*ip) - ETHER_HDR_LEN; + tcp_csum = lro->data_csum; + tcp_csum += in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htons(tcplen + IPPROTO_TCP)); + tcp_csum += mxge_csum_generic((uint16_t*)tcp, + tcp->th_off << 2); + tcp_csum = (tcp_csum & 0xffff) + (tcp_csum >> 16); + tcp_csum = (tcp_csum & 0xffff) + (tcp_csum >> 16); +#if 0 + IOLog("pseudo = 0x%x, generic = 0x%x, sum = %x\n", + in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htons(tcplen + IPPROTO_TCP)), + mxge_csum_generic((uint16_t*)tcp, + tcp->th_off << 2), + htons(0xffff ^ tcp_csum)); +#endif + tcp->th_sum = 0xffff ^ tcp_csum; + } + ifp = mgp->ifp; + (*ifp->if_input)(mgp->ifp, lro->m_head); + mgp->lro_queued += lro->append_cnt + 1; + mgp->lro_flushed++; + lro->m_head = NULL; + lro->timestamp = 0; + lro->append_cnt = 0; + SLIST_INSERT_HEAD(&mgp->lro_free, lro, next); +} + +int +mxge_lro_rx(mxge_softc_t *mgp, struct mbuf *m_head, uint32_t csum) +{ + struct ether_header *eh; + struct ip *ip; + struct tcphdr *tcp; + uint32_t *ts_ptr; + struct mbuf *m_nxt, *m_tail; + struct lro_entry *lro; + int hlen, ip_len, tcp_hdr_len, tcp_data_len, tot_len; + int opt_bytes, trim; + uint32_t seq, tmp_csum, device_mtu; + + eh = mtod(m_head, struct ether_header *); + if (eh->ether_type != htons(ETHERTYPE_IP)) + return 1; + ip = (struct ip *) (eh + 1); + if (ip->ip_p != IPPROTO_TCP) + return 1; + + /* ensure there are no options */ + if ((ip->ip_hl << 2) != sizeof (*ip)) + return -1; + + /* .. and the packet is not fragmented */ + if (ip->ip_off & htons(IP_MF|IP_OFFMASK)) + return -1; + + /* verify that the IP header checksum is correct */ + tmp_csum = mxge_csum_generic((uint16_t *)ip, sizeof (*ip)); + if (__predict_false((tmp_csum ^ 0xffff) != 0)) { + mgp->lro_bad_csum++; + return -1; + } + + /* find the TCP header */ + tcp = (struct tcphdr *) (ip + 1); + + /* ensure no bits set besides ack or psh */ + if ((tcp->th_flags & ~(TH_ACK | TH_PUSH)) != 0) + return -1; + + /* check for timestamps. Since the only option we handle are + timestamps, we only have to handle the simple case of + aligned timestamps */ + + opt_bytes = (tcp->th_off << 2) - sizeof (*tcp); + tcp_hdr_len = sizeof (*tcp) + opt_bytes; + ts_ptr = (uint32_t *)(tcp + 1); + if (opt_bytes != 0) { + if (__predict_false(opt_bytes != TCPOLEN_TSTAMP_APPA) || + (*ts_ptr != ntohl(TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP))) + return -1; + } + + ip_len = ntohs(ip->ip_len); + tcp_data_len = ip_len - (tcp->th_off << 2) - sizeof (*ip); + + + /* + * If frame is padded beyond the end of the IP packet, + * then we must trim the extra bytes off the end. + */ + tot_len = m_head->m_pkthdr.len; + trim = tot_len - (ip_len + ETHER_HDR_LEN); + if (trim != 0) { + if (trim < 0) { + /* truncated packet */ + return -1; + } + m_adj(m_head, -trim); + tot_len = m_head->m_pkthdr.len; + } + + m_nxt = m_head; + m_tail = NULL; /* -Wuninitialized */ + while (m_nxt != NULL) { + m_tail = m_nxt; + m_nxt = m_tail->m_next; + } + + hlen = ip_len + ETHER_HDR_LEN - tcp_data_len; + seq = ntohl(tcp->th_seq); + + SLIST_FOREACH(lro, &mgp->lro_active, next) { + if (lro->source_port == tcp->th_sport && + lro->dest_port == tcp->th_dport && + lro->source_ip == ip->ip_src.s_addr && + lro->dest_ip == ip->ip_dst.s_addr) { + /* Try to append it */ + + if (__predict_false(seq != lro->next_seq)) { + /* out of order packet */ + SLIST_REMOVE(&mgp->lro_active, lro, + lro_entry, next); + mxge_lro_flush(mgp, lro); + return -1; + } + + if (lro->timestamp) { + uint32_t tsval = ntohl(*(ts_ptr + 1)); + /* make sure timestamp values are increasing */ + if (__predict_false(lro->tsval > tsval || + *(ts_ptr + 2) == 0)) { + return -1; + } + lro->tsval = tsval; + lro->tsecr = *(ts_ptr + 2); + } + + lro->next_seq += tcp_data_len; + lro->ack_seq = tcp->th_ack; + lro->window = tcp->th_win; + lro->append_cnt++; + if (tcp_data_len == 0) { + m_freem(m_head); + return 0; + } + /* subtract off the checksum of the tcp header + * from the hardware checksum, and add it to the + * stored tcp data checksum. Byteswap the checksum + * if the total length so far is odd + */ + tmp_csum = mxge_csum_generic((uint16_t*)tcp, + tcp_hdr_len); + csum = csum + (tmp_csum ^ 0xffff); + csum = (csum & 0xffff) + (csum >> 16); + csum = (csum & 0xffff) + (csum >> 16); + if (lro->len & 0x1) { + /* Odd number of bytes so far, flip bytes */ + csum = ((csum << 8) | (csum >> 8)) & 0xffff; + } + csum = csum + lro->data_csum; + csum = (csum & 0xffff) + (csum >> 16); + csum = (csum & 0xffff) + (csum >> 16); + lro->data_csum = csum; + + lro->len += tcp_data_len; + + /* adjust mbuf so that m->m_data points to + the first byte of the payload */ + m_adj(m_head, hlen); + /* append mbuf chain */ + lro->m_tail->m_next = m_head; + /* advance the last pointer */ + lro->m_tail = m_tail; + /* flush packet if required */ + device_mtu = mgp->ifp->if_mtu; + if (lro->len > (65535 - device_mtu)) { + SLIST_REMOVE(&mgp->lro_active, lro, + lro_entry, next); + mxge_lro_flush(mgp, lro); + } + return 0; + } + } + + if (SLIST_EMPTY(&mgp->lro_free)) + return -1; + + /* start a new chain */ + lro = SLIST_FIRST(&mgp->lro_free); + SLIST_REMOVE_HEAD(&mgp->lro_free, next); + SLIST_INSERT_HEAD(&mgp->lro_active, lro, next); + lro->source_port = tcp->th_sport; + lro->dest_port = tcp->th_dport; + lro->source_ip = ip->ip_src.s_addr; + lro->dest_ip = ip->ip_dst.s_addr; + lro->next_seq = seq + tcp_data_len; + lro->mss = tcp_data_len; + lro->ack_seq = tcp->th_ack; + lro->window = tcp->th_win; + + /* save the checksum of just the TCP payload by + * subtracting off the checksum of the TCP header from + * the entire hardware checksum + * Since IP header checksum is correct, checksum over + * the IP header is -0. Substracting -0 is unnecessary. + */ + tmp_csum = mxge_csum_generic((uint16_t*)tcp, tcp_hdr_len); + csum = csum + (tmp_csum ^ 0xffff); + csum = (csum & 0xffff) + (csum >> 16); + csum = (csum & 0xffff) + (csum >> 16); + lro->data_csum = csum; + + lro->ip = ip; + /* record timestamp if it is present */ + if (opt_bytes) { + lro->timestamp = 1; + lro->tsval = ntohl(*(ts_ptr + 1)); + lro->tsecr = *(ts_ptr + 2); + } + lro->len = tot_len; + lro->m_head = m_head; + lro->m_tail = m_tail; + return 0; +} +/* + This file uses Myri10GE driver indentation. + + Local Variables: + c-file-style:"linux" + tab-width:8 + End: +*/ Property changes on: stable/6/sys/dev/mxge/mxge_lro.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/mxge/mxge_mcp.h =================================================================== --- stable/6/sys/dev/mxge/mxge_mcp.h (nonexistent) +++ stable/6/sys/dev/mxge/mxge_mcp.h (revision 170009) @@ -0,0 +1,332 @@ +/******************************************************************************* + +Copyright (c) 2006, Myricom Inc. +All rights reserved. + +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. Neither the name of the Myricom Inc, nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$FreeBSD$ +***************************************************************************/ + +#ifndef _myri10ge_mcp_h +#define _myri10ge_mcp_h + +#define MXGEFW_VERSION_MAJOR 1 +#define MXGEFW_VERSION_MINOR 4 + +#ifdef MXGEFW +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +#endif + +/* 8 Bytes */ +struct mcp_dma_addr { + uint32_t high; + uint32_t low; +}; +typedef struct mcp_dma_addr mcp_dma_addr_t; + +/* 4 Bytes */ +struct mcp_slot { + uint16_t checksum; + uint16_t length; +}; +typedef struct mcp_slot mcp_slot_t; + +/* 64 Bytes */ +struct mcp_cmd { + uint32_t cmd; + uint32_t data0; /* will be low portion if data > 32 bits */ + /* 8 */ + uint32_t data1; /* will be high portion if data > 32 bits */ + uint32_t data2; /* currently unused.. */ + /* 16 */ + struct mcp_dma_addr response_addr; + /* 24 */ + uint8_t pad[40]; +}; +typedef struct mcp_cmd mcp_cmd_t; + +/* 8 Bytes */ +struct mcp_cmd_response { + uint32_t data; + uint32_t result; +}; +typedef struct mcp_cmd_response mcp_cmd_response_t; + + + +/* + flags used in mcp_kreq_ether_send_t: + + The SMALL flag is only needed in the first segment. It is raised + for packets that are total less or equal 512 bytes. + + The CKSUM flag must be set in all segments. + + The PADDED flags is set if the packet needs to be padded, and it + must be set for all segments. + + The MXGEFW_FLAGS_ALIGN_ODD must be set if the cumulative + length of all previous segments was odd. +*/ + + +#define MXGEFW_FLAGS_SMALL 0x1 +#define MXGEFW_FLAGS_TSO_HDR 0x1 +#define MXGEFW_FLAGS_FIRST 0x2 +#define MXGEFW_FLAGS_ALIGN_ODD 0x4 +#define MXGEFW_FLAGS_CKSUM 0x8 +#define MXGEFW_FLAGS_TSO_LAST 0x8 +#define MXGEFW_FLAGS_NO_TSO 0x10 +#define MXGEFW_FLAGS_TSO_CHOP 0x10 +#define MXGEFW_FLAGS_TSO_PLD 0x20 + +#define MXGEFW_SEND_SMALL_SIZE 1520 +#define MXGEFW_MAX_MTU 9400 + +union mcp_pso_or_cumlen { + uint16_t pseudo_hdr_offset; + uint16_t cum_len; +}; +typedef union mcp_pso_or_cumlen mcp_pso_or_cumlen_t; + +#define MXGEFW_MAX_SEND_DESC 12 +#define MXGEFW_PAD 2 + +/* 16 Bytes */ +struct mcp_kreq_ether_send { + uint32_t addr_high; + uint32_t addr_low; + uint16_t pseudo_hdr_offset; + uint16_t length; + uint8_t pad; + uint8_t rdma_count; + uint8_t cksum_offset; /* where to start computing cksum */ + uint8_t flags; /* as defined above */ +}; +typedef struct mcp_kreq_ether_send mcp_kreq_ether_send_t; + +/* 8 Bytes */ +struct mcp_kreq_ether_recv { + uint32_t addr_high; + uint32_t addr_low; +}; +typedef struct mcp_kreq_ether_recv mcp_kreq_ether_recv_t; + + +/* Commands */ + +#define MXGEFW_BOOT_HANDOFF 0xfc0000 +#define MXGEFW_BOOT_DUMMY_RDMA 0xfc01c0 + +#define MXGEFW_ETH_CMD 0xf80000 +#define MXGEFW_ETH_SEND_4 0x200000 +#define MXGEFW_ETH_SEND_1 0x240000 +#define MXGEFW_ETH_SEND_2 0x280000 +#define MXGEFW_ETH_SEND_3 0x2c0000 +#define MXGEFW_ETH_RECV_SMALL 0x300000 +#define MXGEFW_ETH_RECV_BIG 0x340000 + +#define MXGEFW_ETH_SEND(n) (0x200000 + (((n) & 0x03) * 0x40000)) +#define MXGEFW_ETH_SEND_OFFSET(n) (MXGEFW_ETH_SEND(n) - MXGEFW_ETH_SEND_4) + +enum myri10ge_mcp_cmd_type { + MXGEFW_CMD_NONE = 0, + /* Reset the mcp, it is left in a safe state, waiting + for the driver to set all its parameters */ + MXGEFW_CMD_RESET, + + /* get the version number of the current firmware.. + (may be available in the eeprom strings..? */ + MXGEFW_GET_MCP_VERSION, + + + /* Parameters which must be set by the driver before it can + issue MXGEFW_CMD_ETHERNET_UP. They persist until the next + MXGEFW_CMD_RESET is issued */ + + MXGEFW_CMD_SET_INTRQ_DMA, + MXGEFW_CMD_SET_BIG_BUFFER_SIZE, /* in bytes, power of 2 */ + MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, /* in bytes */ + + + /* Parameters which refer to lanai SRAM addresses where the + driver must issue PIO writes for various things */ + + MXGEFW_CMD_GET_SEND_OFFSET, + MXGEFW_CMD_GET_SMALL_RX_OFFSET, + MXGEFW_CMD_GET_BIG_RX_OFFSET, + MXGEFW_CMD_GET_IRQ_ACK_OFFSET, + MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, + + /* Parameters which refer to rings stored on the MCP, + and whose size is controlled by the mcp */ + + MXGEFW_CMD_GET_SEND_RING_SIZE, /* in bytes */ + MXGEFW_CMD_GET_RX_RING_SIZE, /* in bytes */ + + /* Parameters which refer to rings stored in the host, + and whose size is controlled by the host. Note that + all must be physically contiguous and must contain + a power of 2 number of entries. */ + + MXGEFW_CMD_SET_INTRQ_SIZE, /* in bytes */ + + /* command to bring ethernet interface up. Above parameters + (plus mtu & mac address) must have been exchanged prior + to issuing this command */ + MXGEFW_CMD_ETHERNET_UP, + + /* command to bring ethernet interface down. No further sends + or receives may be processed until an MXGEFW_CMD_ETHERNET_UP + is issued, and all interrupt queues must be flushed prior + to ack'ing this command */ + + MXGEFW_CMD_ETHERNET_DOWN, + + /* commands the driver may issue live, without resetting + the nic. Note that increasing the mtu "live" should + only be done if the driver has already supplied buffers + sufficiently large to handle the new mtu. Decreasing + the mtu live is safe */ + + MXGEFW_CMD_SET_MTU, + MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, /* in microseconds */ + MXGEFW_CMD_SET_STATS_INTERVAL, /* in microseconds */ + MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, /* replaced by SET_STATS_DMA_V2 */ + + MXGEFW_ENABLE_PROMISC, + MXGEFW_DISABLE_PROMISC, + MXGEFW_SET_MAC_ADDRESS, + + MXGEFW_ENABLE_FLOW_CONTROL, + MXGEFW_DISABLE_FLOW_CONTROL, + + /* do a DMA test + data0,data1 = DMA address + data2 = RDMA length (MSH), WDMA length (LSH) + command return data = repetitions (MSH), 0.5-ms ticks (LSH) + */ + MXGEFW_DMA_TEST, + + MXGEFW_ENABLE_ALLMULTI, + MXGEFW_DISABLE_ALLMULTI, + + /* returns MXGEFW_CMD_ERROR_MULTICAST + if there is no room in the cache + data0,MSH(data1) = multicast group address */ + MXGEFW_JOIN_MULTICAST_GROUP, + /* returns MXGEFW_CMD_ERROR_MULTICAST + if the address is not in the cache, + or is equal to FF-FF-FF-FF-FF-FF + data0,MSH(data1) = multicast group address */ + MXGEFW_LEAVE_MULTICAST_GROUP, + MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, + + MXGEFW_CMD_SET_STATS_DMA_V2, + /* data0, data1 = bus addr, + data2 = sizeof(struct mcp_irq_data) from driver point of view, allows + adding new stuff to mcp_irq_data without changing the ABI */ + + MXGEFW_CMD_UNALIGNED_TEST, + /* same than DMA_TEST (same args) but abort with UNALIGNED on unaligned + chipset */ + + MXGEFW_CMD_UNALIGNED_STATUS, + /* return data = boolean, true if the chipset is known to be unaligned */ + + MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, + /* data0 = number of big buffers to use. It must be 0 or a power of 2. + * 0 indicates that the NIC consumes as many buffers as they are required + * for packet. This is the default behavior. + * A power of 2 number indicates that the NIC always uses the specified + * number of buffers for each big receive packet. + * It is up to the driver to ensure that this value is big enough for + * the NIC to be able to receive maximum-sized packets. + */ +}; +typedef enum myri10ge_mcp_cmd_type myri10ge_mcp_cmd_type_t; + + +enum myri10ge_mcp_cmd_status { + MXGEFW_CMD_OK = 0, + MXGEFW_CMD_UNKNOWN, + MXGEFW_CMD_ERROR_RANGE, + MXGEFW_CMD_ERROR_BUSY, + MXGEFW_CMD_ERROR_EMPTY, + MXGEFW_CMD_ERROR_CLOSED, + MXGEFW_CMD_ERROR_HASH_ERROR, + MXGEFW_CMD_ERROR_BAD_PORT, + MXGEFW_CMD_ERROR_RESOURCES, + MXGEFW_CMD_ERROR_MULTICAST, + MXGEFW_CMD_ERROR_UNALIGNED +}; +typedef enum myri10ge_mcp_cmd_status myri10ge_mcp_cmd_status_t; + + +#define MXGEFW_OLD_IRQ_DATA_LEN 40 + +struct mcp_irq_data { + /* add new counters at the beginning */ + uint32_t future_use[1]; + uint32_t dropped_pause; + uint32_t dropped_unicast_filtered; + uint32_t dropped_bad_crc32; + uint32_t dropped_bad_phy; + uint32_t dropped_multicast_filtered; +/* 40 Bytes */ + uint32_t send_done_count; + +#define MXGEFW_LINK_DOWN 0 +#define MXGEFW_LINK_UP 1 +#define MXGEFW_LINK_MYRINET 2 +#define MXGEFW_LINK_UNKNOWN 3 + uint32_t link_up; + uint32_t dropped_link_overflow; + uint32_t dropped_link_error_or_filtered; + uint32_t dropped_runt; + uint32_t dropped_overrun; + uint32_t dropped_no_small_buffer; + uint32_t dropped_no_big_buffer; + uint32_t rdma_tags_available; + + uint8_t tx_stopped; + uint8_t link_down; + uint8_t stats_updated; + uint8_t valid; +}; +typedef struct mcp_irq_data mcp_irq_data_t; + + +#endif /* _myri10ge_mcp_h */ Property changes on: stable/6/sys/dev/mxge/mxge_mcp.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/modules/mxge/mxge/Makefile =================================================================== --- stable/6/sys/modules/mxge/mxge/Makefile (nonexistent) +++ stable/6/sys/modules/mxge/mxge/Makefile (revision 170009) @@ -0,0 +1,8 @@ +#$FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/mxge + +KMOD= if_mxge +SRCS= if_mxge.c mxge_lro.c device_if.h bus_if.h pci_if.h + +.include Property changes on: stable/6/sys/modules/mxge/mxge/Makefile ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property