Date: Thu, 12 Jan 2006 18:47:00 +0100 From: Harald Schmalzbauer <harry@schmalzbauer.de> To: cvs-src@freebsd.org Cc: freebsd-current@freebsd.org Subject: Re: cvs commit: src/sys/dev/em if_em.c if_em.h Message-ID: <200601121847.08044@harrymail> In-Reply-To: <200601110030.k0B0UPOx009098@repoman.freebsd.org> References: <200601110030.k0B0UPOx009098@repoman.freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
--nextPart1613906.PzIa2BOAlf Content-Type: multipart/mixed; boundary="Boundary-01=_WYpxDx9t7bKI4jf" Content-Transfer-Encoding: 7bit Content-Disposition: inline --Boundary-01=_WYpxDx9t7bKI4jf Content-Type: text/plain; charset="iso-8859-15" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Am Mittwoch, 11. Januar 2006 01:30 CEST schrieb Scott Long: > scottl 2006-01-11 00:30:25 UTC > > FreeBSD src repository > > Modified files: > sys/dev/em if_em.c if_em.h > Log: > Significant performance improvements for the if_em driver: > > - Only update the rx ring consumer pointer after running through the > rx loop, not with each iteration through the loop. > - If possible, use a fast interupt handler instead of an ithread > handler. Use the interrupt handler to check and squelch the interrupt, > then schedule a taskqueue to do the actual work. This has three > benefits: Thank you very much for your work! Since I'm using many em cards and can't= =20 get higher tranfer rates compared to fxp (but I wanted to double it so I=20 spent some money to replace F-Eth equipment with GbE) I'd like to apply=20 your work to RELENG_6. I made the attached diff, but compiler stops here: /usr/obj/GUNE/usr/src/make.i386/make -V CFILES -V SYSTEM_CFILES -V=20 GEN_CFILES | MKDEP_CPP=3D"cc -E" CC=3D"cc" xargs mkdep -a -f .newdep -O2=20 =2Dpipe -fno-strict-aliasing -march=3Dpentium3 -Wall -Wredundant-decls=20 =2DWnested-externs -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arit= h=20 =2DWinline -Wcast-qual -fformat-extensions -std=3Dc99 -g -nostdinc -I- -I= =2E=20 =2DI/usr/src/sys -I/usr/src/sys/contrib/altq -I/usr/src/sys/contrib/ipfilte= r=20 =2DI/usr/src/sys/contrib/pf -I/usr/src/sys/contrib/dev/ath=20 =2DI/usr/src/sys/contrib/dev/ath/freebsd -I/usr/src/sys/contrib/ngatm=20 =2DI/usr/src/sys/dev/twa -D_KERNEL -DHAVE_KERNEL_OPTION_HEADERS -include=20 opt_global.h -fno-common -finline-limit=3D8000 --param=20 inline-unit-growth=3D100 --param large-function-growth=3D1000 =20 =2Dmno-align-long-strings -mpreferred-stack-boundary=3D2 -mno-mmx -mno-3dn= ow=20 =2Dmno-sse -mno-sse2 -ffreestanding /usr/src/sys/dev/em/if_em.c:3178:34: macro "VLAN_INPUT_TAG" requires 4=20 arguments, but only 3 given mkdep: compile failed *** Error code 1 Stop in /usr/obj/GUNE/usr/src/sys/CUV-LV. *** Error code 1 I have no idea how to correct this, could you please help me? Or isn't it=20 that simple, eg. VLAN isn't the same level in RELENG_6 and -current? Thanks in advance, =2DHarry > Revision Changes Path > 1.98 +193 -18 src/sys/dev/em/if_em.c > 1.41 +10 -1 src/sys/dev/em/if_em.h > _______________________________________________ > cvs-src@freebsd.org mailing list > http://lists.freebsd.org/mailman/listinfo/cvs-src > To unsubscribe, send any mail to "cvs-src-unsubscribe@freebsd.org" --Boundary-01=_WYpxDx9t7bKI4jf Content-Type: text/x-diff; charset="iso-8859-15"; name="if_em.c.diff" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="if_em.c.diff" =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /usr/local/www/cvsroot/FreeBSD/src/sys/dev/em/if_em.c,v retrieving revision 1.65.2.10 retrieving revision 1.98 diff -u -p -r1.65.2.10 -r1.98 =2D-- sys/dev/em/if_em.c 2006/01/10 10:09:03 1.65.2.10 +++ sys/dev/em/if_em.c 2006/01/11 00:30:25 1.98 @@ -31,7 +31,7 @@ POSSIBILITY OF SUCH DAMAGE. =20 **************************************************************************= */ =20 =2D/*$FreeBSD: src/sys/dev/em/if_em.c,v 1.65.2.10 2006/01/10 10:09:03 glebi= us Exp $*/ +/*$FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/dev/em/if_em.c,v 1.98 2= 006/01/11 00:30:25 scottl Exp $*/ =20 #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" @@ -141,6 +141,9 @@ static int em_shutdown(device_t); static int em_suspend(device_t); static int em_resume(device_t); static void em_intr(void *); +#ifndef NO_EM_FASTINTR +static void em_intr_fast(void *); +#endif static void em_start(struct ifnet *); static void em_start_locked(struct ifnet *ifp); static int em_ioctl(struct ifnet *, u_long, caddr_t); @@ -168,7 +171,10 @@ static void em_update_stats_counters(str static void em_clean_transmit_interrupts(struct adapter *); static int em_allocate_receive_structures(struct adapter *); static int em_allocate_transmit_structures(struct adapter *); =2Dstatic void em_process_receive_interrupts(struct adapter *, int); +static int em_process_receive_interrupts(struct adapter *, int); +#ifndef __NO_STRICT_ALIGNMENT +static int em_fixup_rx(struct adapter *); +#endif static void em_receive_checksum(struct adapter *,=20 struct em_rx_desc *, struct mbuf *); @@ -206,6 +212,12 @@ static int em_sysctl_int_delay(SYSCTL_H static void em_add_int_delay_sysctl(struct adapter *, const char *, const char *, struct em_int_delay_info *, int, int); +#ifndef NO_EM_FASTINTR +static void em_add_int_process_limit(struct adapter *, const char *, + const char *, int *, int); +static void em_handle_rxtx(void *context, int pending); +static void em_handle_link(void *context, int pending); +#endif #ifdef DEVICE_POLLING static poll_handler_t em_poll; #endif @@ -254,6 +266,10 @@ TUNABLE_INT("hw.em.tx_abs_int_delay", &e TUNABLE_INT("hw.em.rx_abs_int_delay", &em_rx_abs_int_delay_dflt); TUNABLE_INT("hw.em.rxd", &em_rxd); TUNABLE_INT("hw.em.txd", &em_txd); +#ifndef NO_EM_FASTINTR +static int em_rx_process_limit =3D 100; +TUNABLE_INT("hw.em.rx_process_limit", &em_rx_process_limit); +#endif =20 /********************************************************************* * Device identification routine @@ -376,6 +392,13 @@ em_attach(device_t dev) em_tx_abs_int_delay_dflt); } =20 + /* Sysctls for limiting the amount of work done in the taskqueue */ +#ifndef NO_EM_FASTINTR + em_add_int_process_limit(adapter, "rx_processing_limit", + "max number of rx packets to process", &adapter->rx_process_limit, + em_rx_process_limit); +#endif + /* * Validate number of transmit and receive descriptors. It * must not exceed hardware maximum, and must be multiple @@ -430,7 +453,6 @@ em_attach(device_t dev) */ adapter->hw.report_tx_early =3D 1; =20 =2D if (em_allocate_pci_resources(adapter)) { printf("em%d: Allocation of PCI resources failed\n",=20 adapter->unit); @@ -560,6 +582,17 @@ em_detach(device_t dev) ether_poll_deregister(ifp); #endif =20 + if (adapter->res_interrupt !=3D NULL) { + bus_teardown_intr(dev, adapter->res_interrupt,=20 + adapter->int_handler_tag); + bus_release_resource(dev, SYS_RES_IRQ, 0,=20 + adapter->res_interrupt); + adapter->res_interrupt =3D NULL; + if (adapter->tq !=3D NULL) { + taskqueue_drain(adapter->tq, &adapter->rxtx_task); + taskqueue_drain(taskqueue_fast, &adapter->link_task); + } + } EM_LOCK(adapter); adapter->in_detach =3D 1; em_stop(adapter); @@ -723,16 +756,6 @@ em_ioctl(struct ifnet *ifp, u_long comma break; case SIOCSIFMTU: { =2D#ifndef __NO_STRICT_ALIGNMENT =2D if (ifr->ifr_mtu > ETHERMTU) =2D /* =2D * XXX =2D * Due to the limitation of DMA engine, it needs fix-up =2D * code for strict alignment architectures. Disable =2D * jumbo frame until we have better solutions. =2D */ =2D error =3D EINVAL; =2D#else int max_frame_size; =20 IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFMTU (Set Interface MTU)"); @@ -761,7 +784,6 @@ em_ioctl(struct ifnet *ifp, u_long comma ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN; em_init_locked(adapter); EM_UNLOCK(adapter); =2D#endif break; } case SIOCSIFFLAGS: @@ -1060,11 +1082,113 @@ em_poll(struct ifnet *ifp, enum poll_cmd } #endif /* DEVICE_POLLING */ =20 +#ifndef NO_EM_FASTINTR +static void +em_handle_link(void *context, int pending) +{ + struct adapter *adapter =3D context; + struct ifnet *ifp; + + ifp =3D adapter->ifp; + + EM_LOCK(adapter); + + callout_stop(&adapter->timer); + adapter->hw.get_link_status =3D 1; + em_check_for_link(&adapter->hw); + em_print_link_status(adapter); + callout_reset(&adapter->timer, hz, em_local_timer, + adapter); + EM_UNLOCK(adapter); +} + +static void +em_handle_rxtx(void *context, int pending) +{ + struct adapter *adapter =3D context; + struct ifnet *ifp; + + ifp =3D adapter->ifp; + + /* + * TODO: + * It should be possible to run the tx clean loop without the lock. + */ + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (em_process_receive_interrupts(adapter, + adapter->rx_process_limit) !=3D 0) + taskqueue_enqueue(adapter->tq, &adapter->rxtx_task); + EM_LOCK(adapter); + em_clean_transmit_interrupts(adapter); + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + em_start_locked(ifp); + EM_UNLOCK(adapter); + } + + em_enable_intr(adapter); + return; +} +#endif + /********************************************************************* * * Interrupt Service routine =20 * **********************************************************************/ +#ifndef NO_EM_FASTINTR +static void +em_intr_fast(void *arg) +{ + struct adapter *adapter =3D arg; + struct ifnet *ifp; + uint32_t reg_icr; + + ifp =3D adapter->ifp; =20 + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + return; + } +#endif /* DEVICE_POLLING */ + + reg_icr =3D E1000_READ_REG(&adapter->hw, ICR); + + /* Hot eject? */ + if (reg_icr =3D=3D 0xffffffff) + return; + + /* Definitely not our interrupt. */ + if (reg_icr =3D=3D 0x0) + return; + + /* + * Starting with the 82571 chip, bit 31 should be used to + * determine whether the interrupt belongs to us. + */ + if (adapter->hw.mac_type >=3D em_82571 && + (reg_icr & E1000_ICR_INT_ASSERTED) =3D=3D 0) + return; + + /* + * Mask interrupts until the taskqueue is finished running. This is + * cheap, just assume that it is needed. This also works around the + * MSI message reordering errata on certain systems. + */ + em_disable_intr(adapter); + taskqueue_enqueue(adapter->tq, &adapter->rxtx_task); + + /* Link status change */ + if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) + taskqueue_enqueue(taskqueue_fast, &adapter->link_task); + + if (reg_icr & E1000_ICR_RXO) { + adapter->rx_overruns++; + } + return; +} +#endif + static void em_intr(void *arg) { @@ -1865,7 +1989,7 @@ em_allocate_pci_resources(struct adapter =20 if (adapter->hw.mac_type > em_82543) { /* Figure our where our IO BAR is ? */ =2D for (rid =3D PCIR_BAR(0); rid < PCIR_CARDBUSCIS;) { + for (rid =3D PCIR_BAR(0); rid < PCIR_CIS;) { val =3D pci_read_config(dev, rid, 4); if (E1000_BAR_TYPE(val) =3D=3D E1000_BAR_TYPE_IO) { adapter->io_rid =3D rid; @@ -1876,7 +2000,7 @@ em_allocate_pci_resources(struct adapter if (E1000_BAR_MEM_TYPE(val) =3D=3D E1000_BAR_MEM_TYPE_64BIT) rid +=3D 4; } =2D if (rid >=3D PCIR_CARDBUSCIS) { + if (rid >=3D PCIR_CIS) { printf("em%d: Unable to locate IO BAR\n", adapter->unit); return (ENXIO); } @@ -1905,13 +2029,40 @@ em_allocate_pci_resources(struct adapter adapter->unit); return(ENXIO); } + + /* + * Try allocating a fast interrupt and the associated deferred + * processing contexts. If that doesn't work, try just using an + * ithread. + */ +#ifndef NO_EM_FASTINTR if (bus_setup_intr(dev, adapter->res_interrupt, =2D INTR_TYPE_NET | INTR_MPSAFE, =2D (void (*)(void *)) em_intr, adapter, =2D &adapter->int_handler_tag)) { =2D printf("em%d: Error registering interrupt handler!\n",=20 =2D adapter->unit); =2D return(ENXIO); + INTR_TYPE_NET | INTR_FAST, em_intr_fast, adapter, + &adapter->int_handler_tag) =3D=3D 0) { + + /* Init the deferred processing contexts. */ + TASK_INIT(&adapter->rxtx_task, 0, em_handle_rxtx, adapter); + TASK_INIT(&adapter->link_task, 0, em_handle_link, adapter); + adapter->tq =3D taskqueue_create_fast("em_taskq", M_NOWAIT, + taskqueue_thread_enqueue, + &adapter->tq, &adapter->tqproc); + kthread_create(taskqueue_thread_loop, + &adapter->tq, &adapter->tqproc, + 0, 0, "%s taskq", device_get_nameunit(adapter->dev)); + mtx_lock_spin(&sched_lock); + sched_prio(FIRST_THREAD_IN_PROC(adapter->tqproc), PI_NET); + mtx_unlock_spin(&sched_lock); + } +#endif + if (adapter->int_handler_tag =3D=3D NULL) { + if (bus_setup_intr(dev, adapter->res_interrupt, + INTR_TYPE_NET | INTR_MPSAFE, + em_intr, adapter, + &adapter->int_handler_tag)) { + printf("em%d: Error registering interrupt handler!\n",=20 + adapter->unit); + return(ENXIO); + } } =20 adapter->hw.back =3D &adapter->osdep; @@ -1924,6 +2075,9 @@ em_free_pci_resources(struct adapter * a { device_t dev =3D adapter->dev; =20 + if (adapter->tq !=3D NULL) { + taskqueue_free(adapter->tq); + } if (adapter->res_interrupt !=3D NULL) { bus_teardown_intr(dev, adapter->res_interrupt,=20 adapter->int_handler_tag); @@ -2897,7 +3051,7 @@ em_free_receive_structures(struct adapte * count < 0. * *********************************************************************/ =2Dstatic void +static int em_process_receive_interrupts(struct adapter * adapter, int count) { struct ifnet *ifp; @@ -2910,8 +3064,6 @@ em_process_receive_interrupts(struct ada /* Pointer to the receive descriptor being examined. */ struct em_rx_desc *current_desc; =20 =2D mtx_assert(&adapter->mtx, MA_OWNED); =2D ifp =3D adapter->ifp; i =3D adapter->next_rx_desc_to_check; current_desc =3D &adapter->rx_desc_base[i]; @@ -2919,7 +3071,7 @@ em_process_receive_interrupts(struct ada BUS_DMASYNC_POSTREAD); =20 if (!((current_desc->status) & E1000_RXD_STAT_DD)) { =2D return; + return (0); } =20 while ((current_desc->status & E1000_RXD_STAT_DD) && @@ -3014,12 +3166,19 @@ em_process_receive_interrupts(struct ada ifp->if_ipackets++; em_receive_checksum(adapter, current_desc, adapter->fmp); +#ifndef __NO_STRICT_ALIGNMENT + if (ifp->if_mtu > ETHERMTU && + em_fixup_rx(adapter) !=3D 0) + goto skip; + +#endif if (current_desc->status & E1000_RXD_STAT_= VP) VLAN_INPUT_TAG(ifp, adapter->fmp, (le16toh(current_desc->special) & =2D E1000_RXD_SPC_VLAN_MASK), =2D adapter->fmp =3D NULL); =2D + E1000_RXD_SPC_VLAN_MASK)); +#ifndef __NO_STRICT_ALIGNMENT +skip: +#endif m =3D adapter->fmp; adapter->fmp =3D NULL; adapter->lmp =3D NULL; @@ -3038,24 +3197,75 @@ em_process_receive_interrupts(struct ada bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); =20 =2D /* Advance the E1000's Receive Queue #0 "Tail Pointer". */ =2D E1000_WRITE_REG(&adapter->hw, RDT, i); =2D /* Advance our pointers to the next descriptor */ if (++i =3D=3D adapter->num_rx_desc) i =3D 0; if (m !=3D NULL) { adapter->next_rx_desc_to_check =3D i; =2D EM_UNLOCK(adapter); (*ifp->if_input)(ifp, m); =2D EM_LOCK(adapter); i =3D adapter->next_rx_desc_to_check; } current_desc =3D &adapter->rx_desc_base[i]; } adapter->next_rx_desc_to_check =3D i; =2D return; + + /* Advance the E1000's Receive Queue #0 "Tail Pointer". */ + if (--i < 0) i =3D adapter->num_rx_desc - 1; + E1000_WRITE_REG(&adapter->hw, RDT, i); + + if (!((current_desc->status) & E1000_RXD_STAT_DD)) { + return (0); + } + return (1); +} + +#ifndef __NO_STRICT_ALIGNMENT +/* + * When jumbo frames are enabled we should realign entire payload on + * architecures with strict alignment. This is serious design mistake of 8= 254x + * as it nullifies DMA operations. 8254x just allows RX buffer size to be + * 2048/4096/8192/16384. What we really want is 2048 - ETHER_ALIGN to alig= n its + * payload. On architecures without strict alignment restrictions 8254x st= ill + * performs unaligned memory access which would reduce the performance too= =2E=20 + * To avoid copying over an entire frame to align, we allocate a new mbuf = and + * copy ethernet header to the new mbuf. The new mbuf is prepended into the + * existing mbuf chain. + * + * Be aware, best performance of the 8254x is achived only when jumbo fram= e is + * not used at all on architectures with strict alignment. + */ +static int +em_fixup_rx(struct adapter *adapter) +{ + struct mbuf *m, *n; + int error; + + error =3D 0; + m =3D adapter->fmp; + if (m->m_len <=3D (MCLBYTES - ETHER_HDR_LEN)) { + bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len); + m->m_data +=3D ETHER_HDR_LEN; + } else { + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (n !=3D NULL) { + bcopy(m->m_data, n->m_data, ETHER_HDR_LEN); + m->m_data +=3D ETHER_HDR_LEN; + m->m_len -=3D ETHER_HDR_LEN; + n->m_len =3D ETHER_HDR_LEN; + M_MOVE_PKTHDR(n, m); + n->m_next =3D m; + adapter->fmp =3D n; + } else { + adapter->dropped_pkts++; + m_freem(adapter->fmp); + adapter->fmp =3D NULL; + error =3D ENOMEM; + } + } + + return (error); } +#endif =20 /********************************************************************* * @@ -3563,3 +3773,15 @@ em_add_int_delay_sysctl(struct adapter * OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, info, 0, em_sysctl_int_delay, "I", description); } + +#ifndef NO_EM_FASTINTR +static void +em_add_int_process_limit(struct adapter *adapter, const char *name, + const char *description, int *limit, int value) +{ + *limit =3D value; + SYSCTL_ADD_INT(device_get_sysctl_ctx(adapter->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)), + OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, limit, value, description); +} +#endif --Boundary-01=_WYpxDx9t7bKI4jf Content-Type: text/x-diff; charset="iso-8859-15"; name="if_em.h.diff" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="if_em.h.diff" =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /usr/local/www/cvsroot/FreeBSD/src/sys/dev/em/if_em.h,v retrieving revision 1.32.2.2 retrieving revision 1.41 diff -u -p -r1.32.2.2 -r1.41 =2D-- sys/dev/em/if_em.h 2005/11/25 14:11:59 1.32.2.2 +++ sys/dev/em/if_em.h 2006/01/11 00:30:25 1.41 @@ -31,7 +31,7 @@ POSSIBILITY OF SUCH DAMAGE. =20 **************************************************************************= */ =20 =2D/*$FreeBSD: src/sys/dev/em/if_em.h,v 1.32.2.2 2005/11/25 14:11:59 glebiu= s Exp $*/ +/*$FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/dev/em/if_em.h,v 1.41 2= 006/01/11 00:30:25 scottl Exp $*/ =20 #ifndef _EM_H_DEFINED_ #define _EM_H_DEFINED_ @@ -48,6 +48,10 @@ POSSIBILITY OF SUCH DAMAGE. #include <sys/socket.h> #include <sys/sockio.h> #include <sys/sysctl.h> +#include <sys/taskqueue.h> +#include <sys/kthread.h> +#include <sys/proc.h> +#include <sys/sched.h> =20 #include <machine/bus.h> #include <sys/rman.h> @@ -334,6 +338,10 @@ struct adapter { u_int8_t unit; struct mtx mtx; int em_insert_vlan_header; + struct task link_task; + struct task rxtx_task; + struct taskqueue *tq; /* private task queue */ + struct proc *tqproc; /* thread handling sc_tq */ =20 /* Info about the board itself */ u_int32_t part_num; @@ -378,8 +386,9 @@ struct adapter { struct em_dma_alloc rxdma; /* bus_dma glue for rx desc */ struct em_rx_desc *rx_desc_base; u_int32_t next_rx_desc_to_check; =2D u_int16_t num_rx_desc; u_int32_t rx_buffer_len; + u_int16_t num_rx_desc; + int rx_process_limit; struct em_buffer *rx_buffer_area; bus_dma_tag_t rxtag; =20 --Boundary-01=_WYpxDx9t7bKI4jf-- --nextPart1613906.PzIa2BOAlf Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (FreeBSD) iD8DBQBDxpYcBylq0S4AzzwRAumpAJ9PyWXkwZH4q49hhFxlFZ34+3fgJgCfTvVL 7XjVyvKAnwZcFm13M+kbpLg= =xw+A -----END PGP SIGNATURE----- --nextPart1613906.PzIa2BOAlf--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200601121847.08044>