sfxge_port.c revision 280380
1227569Sphilip/*-
2227569Sphilip * Copyright (c) 2010-2011 Solarflare Communications, Inc.
3227569Sphilip * All rights reserved.
4227569Sphilip *
5227569Sphilip * This software was developed in part by Philip Paeps under contract for
6227569Sphilip * Solarflare Communications, Inc.
7227569Sphilip *
8227569Sphilip * Redistribution and use in source and binary forms, with or without
9227569Sphilip * modification, are permitted provided that the following conditions
10227569Sphilip * are met:
11227569Sphilip * 1. Redistributions of source code must retain the above copyright
12227569Sphilip *    notice, this list of conditions and the following disclaimer.
13227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright
14227569Sphilip *    notice, this list of conditions and the following disclaimer in the
15227569Sphilip *    documentation and/or other materials provided with the distribution.
16227569Sphilip *
17227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20227569Sphilip * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27227569Sphilip * SUCH DAMAGE.
28227569Sphilip */
29227569Sphilip
30227569Sphilip#include <sys/cdefs.h>
31227569Sphilip__FBSDID("$FreeBSD: head/sys/dev/sfxge/sfxge_port.c 280380 2015-03-23 15:53:26Z arybchik $");
32227569Sphilip
33227569Sphilip#include <sys/types.h>
34227699Sphilip#include <sys/limits.h>
35227569Sphilip#include <net/ethernet.h>
36227569Sphilip#include <net/if_dl.h>
37227569Sphilip
38227569Sphilip#include "common/efx.h"
39227569Sphilip
40227569Sphilip#include "sfxge.h"
41227569Sphilip
42227569Sphilipstatic int
43227569Sphilipsfxge_mac_stat_update(struct sfxge_softc *sc)
44227569Sphilip{
45227569Sphilip	struct sfxge_port *port = &sc->port;
46227569Sphilip	efsys_mem_t *esmp = &(port->mac_stats.dma_buf);
47227569Sphilip	clock_t now;
48227569Sphilip	unsigned int count;
49227569Sphilip	int rc;
50227569Sphilip
51278248Sarybchik	SFXGE_PORT_LOCK_ASSERT_OWNED(port);
52227569Sphilip
53279351Sarybchik	if (__predict_false(port->init_state != SFXGE_PORT_STARTED)) {
54227569Sphilip		rc = 0;
55227569Sphilip		goto out;
56227569Sphilip	}
57227569Sphilip
58227569Sphilip	now = ticks;
59227569Sphilip	if (now - port->mac_stats.update_time < hz) {
60227569Sphilip		rc = 0;
61227569Sphilip		goto out;
62227569Sphilip	}
63227569Sphilip
64227569Sphilip	port->mac_stats.update_time = now;
65227569Sphilip
66227569Sphilip	/* If we're unlucky enough to read statistics wduring the DMA, wait
67227569Sphilip	 * up to 10ms for it to finish (typically takes <500us) */
68227569Sphilip	for (count = 0; count < 100; ++count) {
69227569Sphilip		EFSYS_PROBE1(wait, unsigned int, count);
70227569Sphilip
71227569Sphilip		/* Synchronize the DMA memory for reading */
72227569Sphilip		bus_dmamap_sync(esmp->esm_tag, esmp->esm_map,
73227569Sphilip		    BUS_DMASYNC_POSTREAD);
74227569Sphilip
75227569Sphilip		/* Try to update the cached counters */
76227569Sphilip		if ((rc = efx_mac_stats_update(sc->enp, esmp,
77272325Sgnn		    port->mac_stats.decode_buf, NULL)) != EAGAIN)
78227569Sphilip			goto out;
79227569Sphilip
80227569Sphilip		DELAY(100);
81227569Sphilip	}
82227569Sphilip
83227569Sphilip	rc = ETIMEDOUT;
84227569Sphilipout:
85272325Sgnn	return (rc);
86227569Sphilip}
87227569Sphilip
88279184Sarybchikuint64_t
89279184Sarybchiksfxge_get_counter(struct ifnet *ifp, ift_counter c)
90279184Sarybchik{
91279184Sarybchik	struct sfxge_softc *sc = ifp->if_softc;
92279184Sarybchik	uint64_t *mac_stats;
93279184Sarybchik	uint64_t val;
94279184Sarybchik
95279184Sarybchik	SFXGE_PORT_LOCK(&sc->port);
96279184Sarybchik
97279184Sarybchik	/* Ignore error and use old values */
98279184Sarybchik	(void)sfxge_mac_stat_update(sc);
99279184Sarybchik
100279184Sarybchik	mac_stats = (uint64_t *)sc->port.mac_stats.decode_buf;
101279184Sarybchik
102279184Sarybchik	switch (c) {
103279184Sarybchik	case IFCOUNTER_IPACKETS:
104279184Sarybchik		val = mac_stats[EFX_MAC_RX_PKTS];
105279184Sarybchik		break;
106279184Sarybchik	case IFCOUNTER_IERRORS:
107279184Sarybchik		val = mac_stats[EFX_MAC_RX_ERRORS];
108279184Sarybchik		break;
109279184Sarybchik	case IFCOUNTER_OPACKETS:
110279184Sarybchik		val = mac_stats[EFX_MAC_TX_PKTS];
111279184Sarybchik		break;
112279184Sarybchik	case IFCOUNTER_OERRORS:
113279184Sarybchik		val = mac_stats[EFX_MAC_TX_ERRORS];
114279184Sarybchik		break;
115279184Sarybchik	case IFCOUNTER_COLLISIONS:
116279184Sarybchik		val = mac_stats[EFX_MAC_TX_SGL_COL_PKTS] +
117279184Sarybchik		      mac_stats[EFX_MAC_TX_MULT_COL_PKTS] +
118279184Sarybchik		      mac_stats[EFX_MAC_TX_EX_COL_PKTS] +
119279184Sarybchik		      mac_stats[EFX_MAC_TX_LATE_COL_PKTS];
120279184Sarybchik		break;
121279184Sarybchik	case IFCOUNTER_IBYTES:
122279184Sarybchik		val = mac_stats[EFX_MAC_RX_OCTETS];
123279184Sarybchik		break;
124279184Sarybchik	case IFCOUNTER_OBYTES:
125279184Sarybchik		val = mac_stats[EFX_MAC_TX_OCTETS];
126279184Sarybchik		break;
127279184Sarybchik	case IFCOUNTER_OMCASTS:
128279184Sarybchik		val = mac_stats[EFX_MAC_TX_MULTICST_PKTS] +
129279184Sarybchik		      mac_stats[EFX_MAC_TX_BRDCST_PKTS];
130279184Sarybchik		break;
131279184Sarybchik	case IFCOUNTER_OQDROPS:
132279184Sarybchik		SFXGE_PORT_UNLOCK(&sc->port);
133279184Sarybchik		return (sfxge_tx_get_drops(sc));
134279184Sarybchik	case IFCOUNTER_IMCASTS:
135279184Sarybchik		/* if_imcasts is maintained in net/if_ethersubr.c */
136279184Sarybchik	case IFCOUNTER_IQDROPS:
137279184Sarybchik		/* if_iqdrops is maintained in net/if_ethersubr.c */
138279184Sarybchik	case IFCOUNTER_NOPROTO:
139279184Sarybchik		/* if_noproto is maintained in net/if_ethersubr.c */
140279184Sarybchik	default:
141279184Sarybchik		SFXGE_PORT_UNLOCK(&sc->port);
142279184Sarybchik		return (if_get_counter_default(ifp, c));
143279184Sarybchik	}
144279184Sarybchik
145279184Sarybchik	SFXGE_PORT_UNLOCK(&sc->port);
146279184Sarybchik
147279184Sarybchik	return (val);
148279184Sarybchik}
149279184Sarybchik
150227569Sphilipstatic int
151227569Sphilipsfxge_mac_stat_handler(SYSCTL_HANDLER_ARGS)
152227569Sphilip{
153227569Sphilip	struct sfxge_softc *sc = arg1;
154227569Sphilip	unsigned int id = arg2;
155227569Sphilip	int rc;
156278838Sarybchik	uint64_t val;
157227569Sphilip
158278248Sarybchik	SFXGE_PORT_LOCK(&sc->port);
159278838Sarybchik	if ((rc = sfxge_mac_stat_update(sc)) == 0)
160278838Sarybchik		val = ((uint64_t *)sc->port.mac_stats.decode_buf)[id];
161278838Sarybchik	SFXGE_PORT_UNLOCK(&sc->port);
162227569Sphilip
163278838Sarybchik	if (rc == 0)
164278838Sarybchik		rc = SYSCTL_OUT(req, &val, sizeof(val));
165278248Sarybchik	return (rc);
166227569Sphilip}
167227569Sphilip
168227569Sphilipstatic void
169227569Sphilipsfxge_mac_stat_init(struct sfxge_softc *sc)
170227569Sphilip{
171227569Sphilip	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
172227569Sphilip	struct sysctl_oid_list *stat_list;
173227569Sphilip	unsigned int id;
174227569Sphilip	const char *name;
175227569Sphilip
176227569Sphilip	stat_list = SYSCTL_CHILDREN(sc->stats_node);
177227569Sphilip
178227569Sphilip	/* Initialise the named stats */
179227569Sphilip	for (id = 0; id < EFX_MAC_NSTATS; id++) {
180227569Sphilip		name = efx_mac_stat_name(sc->enp, id);
181227569Sphilip		SYSCTL_ADD_PROC(
182227569Sphilip			ctx, stat_list,
183227569Sphilip			OID_AUTO, name, CTLTYPE_U64|CTLFLAG_RD,
184227569Sphilip			sc, id, sfxge_mac_stat_handler, "Q",
185227569Sphilip			"");
186227569Sphilip	}
187227569Sphilip}
188227569Sphilip
189227569Sphilip#ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
190227569Sphilip
191227569Sphilipstatic unsigned int
192227569Sphilipsfxge_port_wanted_fc(struct sfxge_softc *sc)
193227569Sphilip{
194227569Sphilip	struct ifmedia_entry *ifm = sc->media.ifm_cur;
195227569Sphilip
196227569Sphilip	if (ifm->ifm_media == (IFM_ETHER | IFM_AUTO))
197272325Sgnn		return (EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE);
198272325Sgnn	return (((ifm->ifm_media & IFM_ETH_RXPAUSE) ? EFX_FCNTL_RESPOND : 0) |
199272325Sgnn		((ifm->ifm_media & IFM_ETH_TXPAUSE) ? EFX_FCNTL_GENERATE : 0));
200227569Sphilip}
201227569Sphilip
202227569Sphilipstatic unsigned int
203227569Sphilipsfxge_port_link_fc_ifm(struct sfxge_softc *sc)
204227569Sphilip{
205227569Sphilip	unsigned int wanted_fc, link_fc;
206227569Sphilip
207227569Sphilip	efx_mac_fcntl_get(sc->enp, &wanted_fc, &link_fc);
208227569Sphilip	return ((link_fc & EFX_FCNTL_RESPOND) ? IFM_ETH_RXPAUSE : 0) |
209227569Sphilip		((link_fc & EFX_FCNTL_GENERATE) ? IFM_ETH_TXPAUSE : 0);
210227569Sphilip}
211227569Sphilip
212227569Sphilip#else /* !SFXGE_HAVE_PAUSE_MEDIAOPTS */
213227569Sphilip
214227569Sphilipstatic unsigned int
215227569Sphilipsfxge_port_wanted_fc(struct sfxge_softc *sc)
216227569Sphilip{
217272325Sgnn	return (sc->port.wanted_fc);
218227569Sphilip}
219227569Sphilip
220227569Sphilipstatic unsigned int
221227569Sphilipsfxge_port_link_fc_ifm(struct sfxge_softc *sc)
222227569Sphilip{
223272325Sgnn	return (0);
224227569Sphilip}
225227569Sphilip
226227569Sphilipstatic int
227227569Sphilipsfxge_port_wanted_fc_handler(SYSCTL_HANDLER_ARGS)
228227569Sphilip{
229227569Sphilip	struct sfxge_softc *sc;
230227569Sphilip	struct sfxge_port *port;
231227569Sphilip	unsigned int fcntl;
232227569Sphilip	int error;
233227569Sphilip
234227569Sphilip	sc = arg1;
235227569Sphilip	port = &sc->port;
236227569Sphilip
237272325Sgnn	if (req->newptr != NULL) {
238227569Sphilip		if ((error = SYSCTL_IN(req, &fcntl, sizeof(fcntl))) != 0)
239278838Sarybchik			return (error);
240227569Sphilip
241278838Sarybchik		SFXGE_PORT_LOCK(port);
242227569Sphilip
243278838Sarybchik		if (port->wanted_fc != fcntl) {
244280380Sarybchik			if (port->init_state == SFXGE_PORT_STARTED)
245278838Sarybchik				error = efx_mac_fcntl_set(sc->enp,
246278838Sarybchik							  port->wanted_fc,
247278838Sarybchik							  B_TRUE);
248278838Sarybchik			if (error == 0)
249278838Sarybchik				port->wanted_fc = fcntl;
250278838Sarybchik		}
251227569Sphilip
252278838Sarybchik		SFXGE_PORT_UNLOCK(port);
253278838Sarybchik	} else {
254278838Sarybchik		SFXGE_PORT_LOCK(port);
255278838Sarybchik		fcntl = port->wanted_fc;
256278838Sarybchik		SFXGE_PORT_UNLOCK(port);
257227569Sphilip
258278838Sarybchik		error = SYSCTL_OUT(req, &fcntl, sizeof(fcntl));
259227569Sphilip	}
260227569Sphilip
261227569Sphilip	return (error);
262227569Sphilip}
263227569Sphilip
264227569Sphilipstatic int
265227569Sphilipsfxge_port_link_fc_handler(SYSCTL_HANDLER_ARGS)
266227569Sphilip{
267227569Sphilip	struct sfxge_softc *sc;
268227569Sphilip	struct sfxge_port *port;
269227569Sphilip	unsigned int wanted_fc, link_fc;
270227569Sphilip
271227569Sphilip	sc = arg1;
272227569Sphilip	port = &sc->port;
273227569Sphilip
274278221Sarybchik	SFXGE_PORT_LOCK(port);
275279351Sarybchik	if (__predict_true(port->init_state == SFXGE_PORT_STARTED) &&
276279351Sarybchik	    SFXGE_LINK_UP(sc))
277227569Sphilip		efx_mac_fcntl_get(sc->enp, &wanted_fc, &link_fc);
278227569Sphilip	else
279227569Sphilip		link_fc = 0;
280278221Sarybchik	SFXGE_PORT_UNLOCK(port);
281227569Sphilip
282278838Sarybchik	return (SYSCTL_OUT(req, &link_fc, sizeof(link_fc)));
283227569Sphilip}
284227569Sphilip
285227569Sphilip#endif /* SFXGE_HAVE_PAUSE_MEDIAOPTS */
286227569Sphilip
287272452Sgnnstatic const uint64_t sfxge_link_baudrate[EFX_LINK_NMODES] = {
288227699Sphilip	[EFX_LINK_10HDX]	= IF_Mbps(10),
289227699Sphilip	[EFX_LINK_10FDX]	= IF_Mbps(10),
290227699Sphilip	[EFX_LINK_100HDX]	= IF_Mbps(100),
291227699Sphilip	[EFX_LINK_100FDX]	= IF_Mbps(100),
292227699Sphilip	[EFX_LINK_1000HDX]	= IF_Gbps(1),
293227699Sphilip	[EFX_LINK_1000FDX]	= IF_Gbps(1),
294279267Sarybchik	[EFX_LINK_10000FDX]	= IF_Gbps(10),
295227569Sphilip};
296227569Sphilip
297227569Sphilipvoid
298227569Sphilipsfxge_mac_link_update(struct sfxge_softc *sc, efx_link_mode_t mode)
299227569Sphilip{
300227569Sphilip	struct sfxge_port *port;
301227569Sphilip	int link_state;
302272325Sgnn
303227569Sphilip	port = &sc->port;
304227569Sphilip
305227569Sphilip	if (port->link_mode == mode)
306227569Sphilip		return;
307227569Sphilip
308227569Sphilip	port->link_mode = mode;
309227569Sphilip
310227569Sphilip	/* Push link state update to the OS */
311227569Sphilip	link_state = (port->link_mode != EFX_LINK_DOWN ?
312227569Sphilip		      LINK_STATE_UP : LINK_STATE_DOWN);
313227699Sphilip	sc->ifnet->if_baudrate = sfxge_link_baudrate[port->link_mode];
314227569Sphilip	if_link_state_change(sc->ifnet, link_state);
315227569Sphilip}
316227569Sphilip
317227569Sphilipstatic void
318227569Sphilipsfxge_mac_poll_work(void *arg, int npending)
319227569Sphilip{
320227569Sphilip	struct sfxge_softc *sc;
321227569Sphilip	efx_nic_t *enp;
322227569Sphilip	struct sfxge_port *port;
323227569Sphilip	efx_link_mode_t mode;
324227569Sphilip
325227569Sphilip	sc = (struct sfxge_softc *)arg;
326227569Sphilip	enp = sc->enp;
327227569Sphilip	port = &sc->port;
328227569Sphilip
329278221Sarybchik	SFXGE_PORT_LOCK(port);
330227569Sphilip
331279351Sarybchik	if (__predict_false(port->init_state != SFXGE_PORT_STARTED))
332227569Sphilip		goto done;
333227569Sphilip
334227569Sphilip	/* This may sleep waiting for MCDI completion */
335227569Sphilip	(void)efx_port_poll(enp, &mode);
336227569Sphilip	sfxge_mac_link_update(sc, mode);
337227569Sphilip
338227569Sphilipdone:
339278221Sarybchik	SFXGE_PORT_UNLOCK(port);
340227569Sphilip}
341227569Sphilip
342227569Sphilipstatic int
343227569Sphilipsfxge_mac_filter_set_locked(struct sfxge_softc *sc)
344227569Sphilip{
345227569Sphilip	unsigned int bucket[EFX_MAC_HASH_BITS];
346227569Sphilip	struct ifnet *ifp = sc->ifnet;
347227569Sphilip	struct ifmultiaddr *ifma;
348227569Sphilip	struct sockaddr_dl *sa;
349227569Sphilip	efx_nic_t *enp = sc->enp;
350227569Sphilip	unsigned int index;
351227569Sphilip	int rc;
352227569Sphilip
353227569Sphilip	/* Set promisc-unicast and broadcast filter bits */
354227569Sphilip	if ((rc = efx_mac_filter_set(enp, !!(ifp->if_flags & IFF_PROMISC),
355227569Sphilip				     B_TRUE)) != 0)
356272325Sgnn		return (rc);
357227569Sphilip
358227569Sphilip	/* Set multicast hash filter */
359227569Sphilip	if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) {
360227569Sphilip		for (index = 0; index < EFX_MAC_HASH_BITS; index++)
361227569Sphilip			bucket[index] = 1;
362227569Sphilip	} else {
363227569Sphilip		/* Broadcast frames also go through the multicast
364227569Sphilip		 * filter, and the broadcast address hashes to
365227569Sphilip		 * 0xff. */
366227569Sphilip		bucket[0xff] = 1;
367227569Sphilip
368229613Sjhb		if_maddr_rlock(ifp);
369227569Sphilip		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
370227569Sphilip			if (ifma->ifma_addr->sa_family == AF_LINK) {
371227569Sphilip				sa = (struct sockaddr_dl *)ifma->ifma_addr;
372227569Sphilip				index = ether_crc32_le(LLADDR(sa), 6) & 0xff;
373227569Sphilip				bucket[index] = 1;
374227569Sphilip			}
375227569Sphilip		}
376229613Sjhb		if_maddr_runlock(ifp);
377227569Sphilip	}
378272325Sgnn	return (efx_mac_hash_set(enp, bucket));
379227569Sphilip}
380227569Sphilip
381227569Sphilipint
382227569Sphilipsfxge_mac_filter_set(struct sfxge_softc *sc)
383227569Sphilip{
384227569Sphilip	struct sfxge_port *port = &sc->port;
385227569Sphilip	int rc;
386227569Sphilip
387278221Sarybchik	SFXGE_PORT_LOCK(port);
388264772Sgnn	/*
389264772Sgnn	 * The function may be called without softc_lock held in the
390264772Sgnn	 * case of SIOCADDMULTI and SIOCDELMULTI ioctls. ioctl handler
391264772Sgnn	 * checks IFF_DRV_RUNNING flag which implies port started, but
392264772Sgnn	 * it is not guaranteed to remain. softc_lock shared lock can't
393264772Sgnn	 * be held in the case of these ioctls processing, since it
394264772Sgnn	 * results in failure where kernel complains that non-sleepable
395264772Sgnn	 * lock is held in sleeping thread. Both problems are repeatable
396264772Sgnn	 * on LAG with LACP proto bring up.
397264772Sgnn	 */
398279351Sarybchik	if (__predict_true(port->init_state == SFXGE_PORT_STARTED))
399264772Sgnn		rc = sfxge_mac_filter_set_locked(sc);
400264772Sgnn	else
401264772Sgnn		rc = 0;
402278221Sarybchik	SFXGE_PORT_UNLOCK(port);
403272325Sgnn	return (rc);
404227569Sphilip}
405227569Sphilip
406227569Sphilipvoid
407227569Sphilipsfxge_port_stop(struct sfxge_softc *sc)
408227569Sphilip{
409227569Sphilip	struct sfxge_port *port;
410227569Sphilip	efx_nic_t *enp;
411227569Sphilip
412227569Sphilip	port = &sc->port;
413227569Sphilip	enp = sc->enp;
414227569Sphilip
415278221Sarybchik	SFXGE_PORT_LOCK(port);
416227569Sphilip
417227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_STARTED,
418227569Sphilip	    ("port not started"));
419227569Sphilip
420227569Sphilip	port->init_state = SFXGE_PORT_INITIALIZED;
421227569Sphilip
422227569Sphilip	port->mac_stats.update_time = 0;
423227569Sphilip
424227569Sphilip	/* This may call MCDI */
425227569Sphilip	(void)efx_mac_drain(enp, B_TRUE);
426227569Sphilip
427227569Sphilip	(void)efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf, 0, B_FALSE);
428227569Sphilip
429227569Sphilip	port->link_mode = EFX_LINK_UNKNOWN;
430227569Sphilip
431227569Sphilip	/* Destroy the common code port object. */
432227569Sphilip	efx_port_fini(sc->enp);
433227569Sphilip
434278221Sarybchik	SFXGE_PORT_UNLOCK(port);
435227569Sphilip}
436227569Sphilip
437227569Sphilipint
438227569Sphilipsfxge_port_start(struct sfxge_softc *sc)
439227569Sphilip{
440227569Sphilip	uint8_t mac_addr[ETHER_ADDR_LEN];
441227569Sphilip	struct ifnet *ifp = sc->ifnet;
442227569Sphilip	struct sfxge_port *port;
443227569Sphilip	efx_nic_t *enp;
444227569Sphilip	size_t pdu;
445227569Sphilip	int rc;
446227569Sphilip
447227569Sphilip	port = &sc->port;
448227569Sphilip	enp = sc->enp;
449227569Sphilip
450278221Sarybchik	SFXGE_PORT_LOCK(port);
451227569Sphilip
452227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_INITIALIZED,
453227569Sphilip	    ("port not initialized"));
454227569Sphilip
455227569Sphilip	/* Initialize the port object in the common code. */
456227569Sphilip	if ((rc = efx_port_init(sc->enp)) != 0)
457227569Sphilip		goto fail;
458227569Sphilip
459227569Sphilip	/* Set the SDU */
460227569Sphilip	pdu = EFX_MAC_PDU(ifp->if_mtu);
461227569Sphilip	if ((rc = efx_mac_pdu_set(enp, pdu)) != 0)
462227569Sphilip		goto fail2;
463227569Sphilip
464227569Sphilip	if ((rc = efx_mac_fcntl_set(enp, sfxge_port_wanted_fc(sc), B_TRUE))
465227569Sphilip	    != 0)
466227569Sphilip		goto fail2;
467227569Sphilip
468227569Sphilip	/* Set the unicast address */
469229613Sjhb	if_addr_rlock(ifp);
470227569Sphilip	bcopy(LLADDR((struct sockaddr_dl *)ifp->if_addr->ifa_addr),
471227569Sphilip	      mac_addr, sizeof(mac_addr));
472229613Sjhb	if_addr_runlock(ifp);
473227569Sphilip	if ((rc = efx_mac_addr_set(enp, mac_addr)) != 0)
474227569Sphilip		goto fail;
475227569Sphilip
476227569Sphilip	sfxge_mac_filter_set_locked(sc);
477227569Sphilip
478227569Sphilip	/* Update MAC stats by DMA every second */
479227569Sphilip	if ((rc = efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf,
480272325Sgnn	    1000, B_FALSE)) != 0)
481227569Sphilip		goto fail2;
482227569Sphilip
483227569Sphilip	if ((rc = efx_mac_drain(enp, B_FALSE)) != 0)
484227569Sphilip		goto fail3;
485227569Sphilip
486227569Sphilip	if ((rc = efx_phy_adv_cap_set(sc->enp, sc->media.ifm_cur->ifm_data))
487227569Sphilip	    != 0)
488227569Sphilip		goto fail4;
489227569Sphilip
490227569Sphilip	port->init_state = SFXGE_PORT_STARTED;
491227569Sphilip
492227569Sphilip	/* Single poll in case there were missing initial events */
493278221Sarybchik	SFXGE_PORT_UNLOCK(port);
494227569Sphilip	sfxge_mac_poll_work(sc, 0);
495227569Sphilip
496227569Sphilip	return (0);
497227569Sphilip
498227569Sphilipfail4:
499227569Sphilip	(void)efx_mac_drain(enp, B_TRUE);
500227569Sphilipfail3:
501227569Sphilip	(void)efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf,
502272325Sgnn	    0, B_FALSE);
503227569Sphilipfail2:
504227569Sphilip	efx_port_fini(sc->enp);
505227569Sphilipfail:
506278221Sarybchik	SFXGE_PORT_UNLOCK(port);
507227569Sphilip
508227569Sphilip	return (rc);
509227569Sphilip}
510227569Sphilip
511227569Sphilipstatic int
512227569Sphilipsfxge_phy_stat_update(struct sfxge_softc *sc)
513227569Sphilip{
514227569Sphilip	struct sfxge_port *port = &sc->port;
515227569Sphilip	efsys_mem_t *esmp = &port->phy_stats.dma_buf;
516227569Sphilip	clock_t now;
517227569Sphilip	unsigned int count;
518227569Sphilip	int rc;
519227569Sphilip
520278248Sarybchik	SFXGE_PORT_LOCK_ASSERT_OWNED(port);
521227569Sphilip
522279351Sarybchik	if (__predict_false(port->init_state != SFXGE_PORT_STARTED)) {
523227569Sphilip		rc = 0;
524227569Sphilip		goto out;
525227569Sphilip	}
526227569Sphilip
527227569Sphilip	now = ticks;
528227569Sphilip	if (now - port->phy_stats.update_time < hz) {
529227569Sphilip		rc = 0;
530227569Sphilip		goto out;
531227569Sphilip	}
532227569Sphilip
533227569Sphilip	port->phy_stats.update_time = now;
534227569Sphilip
535227569Sphilip	/* If we're unlucky enough to read statistics wduring the DMA, wait
536227569Sphilip	 * up to 10ms for it to finish (typically takes <500us) */
537227569Sphilip	for (count = 0; count < 100; ++count) {
538227569Sphilip		EFSYS_PROBE1(wait, unsigned int, count);
539227569Sphilip
540227569Sphilip		/* Synchronize the DMA memory for reading */
541227569Sphilip		bus_dmamap_sync(esmp->esm_tag, esmp->esm_map,
542227569Sphilip		    BUS_DMASYNC_POSTREAD);
543227569Sphilip
544227569Sphilip		/* Try to update the cached counters */
545227569Sphilip		if ((rc = efx_phy_stats_update(sc->enp, esmp,
546227569Sphilip		    port->phy_stats.decode_buf)) != EAGAIN)
547227569Sphilip			goto out;
548227569Sphilip
549227569Sphilip		DELAY(100);
550227569Sphilip	}
551227569Sphilip
552227569Sphilip	rc = ETIMEDOUT;
553227569Sphilipout:
554272325Sgnn	return (rc);
555227569Sphilip}
556227569Sphilip
557227569Sphilipstatic int
558227569Sphilipsfxge_phy_stat_handler(SYSCTL_HANDLER_ARGS)
559227569Sphilip{
560227569Sphilip	struct sfxge_softc *sc = arg1;
561227569Sphilip	unsigned int id = arg2;
562227569Sphilip	int rc;
563278838Sarybchik	uint32_t val;
564227569Sphilip
565278248Sarybchik	SFXGE_PORT_LOCK(&sc->port);
566278838Sarybchik	if ((rc = sfxge_phy_stat_update(sc)) == 0)
567278838Sarybchik		val = ((uint32_t *)sc->port.phy_stats.decode_buf)[id];
568278838Sarybchik	SFXGE_PORT_UNLOCK(&sc->port);
569227569Sphilip
570278838Sarybchik	if (rc == 0)
571278838Sarybchik		rc = SYSCTL_OUT(req, &val, sizeof(val));
572278248Sarybchik	return (rc);
573227569Sphilip}
574227569Sphilip
575227569Sphilipstatic void
576227569Sphilipsfxge_phy_stat_init(struct sfxge_softc *sc)
577227569Sphilip{
578227569Sphilip	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
579227569Sphilip	struct sysctl_oid_list *stat_list;
580227569Sphilip	unsigned int id;
581227569Sphilip	const char *name;
582227569Sphilip	uint64_t stat_mask = efx_nic_cfg_get(sc->enp)->enc_phy_stat_mask;
583227569Sphilip
584227569Sphilip	stat_list = SYSCTL_CHILDREN(sc->stats_node);
585227569Sphilip
586227569Sphilip	/* Initialise the named stats */
587227569Sphilip	for (id = 0; id < EFX_PHY_NSTATS; id++) {
588227569Sphilip		if (!(stat_mask & ((uint64_t)1 << id)))
589227569Sphilip			continue;
590227569Sphilip		name = efx_phy_stat_name(sc->enp, id);
591227569Sphilip		SYSCTL_ADD_PROC(
592227569Sphilip			ctx, stat_list,
593227569Sphilip			OID_AUTO, name, CTLTYPE_UINT|CTLFLAG_RD,
594227569Sphilip			sc, id, sfxge_phy_stat_handler,
595227569Sphilip			id == EFX_PHY_STAT_OUI ? "IX" : "IU",
596227569Sphilip			"");
597227569Sphilip	}
598227569Sphilip}
599227569Sphilip
600227569Sphilipvoid
601227569Sphilipsfxge_port_fini(struct sfxge_softc *sc)
602227569Sphilip{
603227569Sphilip	struct sfxge_port *port;
604227569Sphilip	efsys_mem_t *esmp;
605227569Sphilip
606227569Sphilip	port = &sc->port;
607227569Sphilip	esmp = &port->mac_stats.dma_buf;
608227569Sphilip
609227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_INITIALIZED,
610227569Sphilip	    ("Port not initialized"));
611227569Sphilip
612227569Sphilip	port->init_state = SFXGE_PORT_UNINITIALIZED;
613227569Sphilip
614227569Sphilip	port->link_mode = EFX_LINK_UNKNOWN;
615227569Sphilip
616227569Sphilip	/* Finish with PHY DMA memory */
617227569Sphilip	sfxge_dma_free(&port->phy_stats.dma_buf);
618227569Sphilip	free(port->phy_stats.decode_buf, M_SFXGE);
619227569Sphilip
620227569Sphilip	sfxge_dma_free(esmp);
621227569Sphilip	free(port->mac_stats.decode_buf, M_SFXGE);
622227569Sphilip
623278221Sarybchik	SFXGE_PORT_LOCK_DESTROY(port);
624227569Sphilip
625227569Sphilip	port->sc = NULL;
626227569Sphilip}
627227569Sphilip
628227569Sphilipint
629227569Sphilipsfxge_port_init(struct sfxge_softc *sc)
630227569Sphilip{
631227569Sphilip	struct sfxge_port *port;
632227569Sphilip	struct sysctl_ctx_list *sysctl_ctx;
633227569Sphilip	struct sysctl_oid *sysctl_tree;
634227569Sphilip	efsys_mem_t *mac_stats_buf, *phy_stats_buf;
635227569Sphilip	int rc;
636227569Sphilip
637227569Sphilip	port = &sc->port;
638227569Sphilip	mac_stats_buf = &port->mac_stats.dma_buf;
639227569Sphilip	phy_stats_buf = &port->phy_stats.dma_buf;
640227569Sphilip
641227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_UNINITIALIZED,
642227569Sphilip	    ("Port already initialized"));
643227569Sphilip
644227569Sphilip	port->sc = sc;
645227569Sphilip
646278250Sarybchik	SFXGE_PORT_LOCK_INIT(port, device_get_nameunit(sc->dev));
647227569Sphilip
648227569Sphilip	port->phy_stats.decode_buf = malloc(EFX_PHY_NSTATS * sizeof(uint32_t),
649227569Sphilip					    M_SFXGE, M_WAITOK | M_ZERO);
650227569Sphilip	if ((rc = sfxge_dma_alloc(sc, EFX_PHY_STATS_SIZE, phy_stats_buf)) != 0)
651227569Sphilip		goto fail;
652227569Sphilip	sfxge_phy_stat_init(sc);
653227569Sphilip
654227569Sphilip	sysctl_ctx = device_get_sysctl_ctx(sc->dev);
655227569Sphilip	sysctl_tree = device_get_sysctl_tree(sc->dev);
656227569Sphilip
657227569Sphilip#ifndef SFXGE_HAVE_PAUSE_MEDIAOPTS
658227569Sphilip	/* If flow control cannot be configured or reported through
659227569Sphilip	 * ifmedia, provide sysctls for it. */
660227569Sphilip	port->wanted_fc = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
661227569Sphilip	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
662227569Sphilip	    "wanted_fc", CTLTYPE_UINT|CTLFLAG_RW, sc, 0,
663227569Sphilip	    sfxge_port_wanted_fc_handler, "IU", "wanted flow control mode");
664227569Sphilip	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
665227569Sphilip	    "link_fc", CTLTYPE_UINT|CTLFLAG_RD, sc, 0,
666227569Sphilip	    sfxge_port_link_fc_handler, "IU", "link flow control mode");
667227569Sphilip#endif
668227569Sphilip
669227569Sphilip	port->mac_stats.decode_buf = malloc(EFX_MAC_NSTATS * sizeof(uint64_t),
670227569Sphilip					    M_SFXGE, M_WAITOK | M_ZERO);
671227569Sphilip	if ((rc = sfxge_dma_alloc(sc, EFX_MAC_STATS_SIZE, mac_stats_buf)) != 0)
672227569Sphilip		goto fail2;
673227569Sphilip	sfxge_mac_stat_init(sc);
674227569Sphilip
675227569Sphilip	port->init_state = SFXGE_PORT_INITIALIZED;
676227569Sphilip
677227569Sphilip	return (0);
678227569Sphilip
679227569Sphilipfail2:
680227569Sphilip	free(port->mac_stats.decode_buf, M_SFXGE);
681227569Sphilip	sfxge_dma_free(phy_stats_buf);
682227569Sphilipfail:
683227569Sphilip	free(port->phy_stats.decode_buf, M_SFXGE);
684278221Sarybchik	SFXGE_PORT_LOCK_DESTROY(port);
685227569Sphilip	port->sc = NULL;
686272325Sgnn	return (rc);
687227569Sphilip}
688227569Sphilip
689227569Sphilipstatic int sfxge_link_mode[EFX_PHY_MEDIA_NTYPES][EFX_LINK_NMODES] = {
690227569Sphilip	[EFX_PHY_MEDIA_CX4] = {
691227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_CX4,
692227569Sphilip	},
693227569Sphilip	[EFX_PHY_MEDIA_KX4] = {
694227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_KX4,
695227569Sphilip	},
696227569Sphilip	[EFX_PHY_MEDIA_XFP] = {
697227569Sphilip		/* Don't know the module type, but assume SR for now. */
698227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
699227569Sphilip	},
700227569Sphilip	[EFX_PHY_MEDIA_SFP_PLUS] = {
701227569Sphilip		/* Don't know the module type, but assume SX/SR for now. */
702227569Sphilip		[EFX_LINK_1000FDX]	= IFM_ETHER | IFM_FDX | IFM_1000_SX,
703227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
704227569Sphilip	},
705227569Sphilip	[EFX_PHY_MEDIA_BASE_T] = {
706227569Sphilip		[EFX_LINK_10HDX]	= IFM_ETHER | IFM_HDX | IFM_10_T,
707227569Sphilip		[EFX_LINK_10FDX]	= IFM_ETHER | IFM_FDX | IFM_10_T,
708227569Sphilip		[EFX_LINK_100HDX]	= IFM_ETHER | IFM_HDX | IFM_100_TX,
709227569Sphilip		[EFX_LINK_100FDX]	= IFM_ETHER | IFM_FDX | IFM_100_TX,
710227569Sphilip		[EFX_LINK_1000HDX]	= IFM_ETHER | IFM_HDX | IFM_1000_T,
711227569Sphilip		[EFX_LINK_1000FDX]	= IFM_ETHER | IFM_FDX | IFM_1000_T,
712227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_T,
713227569Sphilip	},
714227569Sphilip};
715227569Sphilip
716227569Sphilipstatic void
717227569Sphilipsfxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
718227569Sphilip{
719227569Sphilip	struct sfxge_softc *sc;
720227569Sphilip	efx_phy_media_type_t medium_type;
721227569Sphilip	efx_link_mode_t mode;
722227569Sphilip
723227569Sphilip	sc = ifp->if_softc;
724278221Sarybchik	SFXGE_ADAPTER_LOCK(sc);
725227569Sphilip
726227569Sphilip	ifmr->ifm_status = IFM_AVALID;
727227569Sphilip	ifmr->ifm_active = IFM_ETHER;
728227569Sphilip
729227569Sphilip	if (SFXGE_RUNNING(sc) && SFXGE_LINK_UP(sc)) {
730227569Sphilip		ifmr->ifm_status |= IFM_ACTIVE;
731227569Sphilip
732227569Sphilip		efx_phy_media_type_get(sc->enp, &medium_type);
733227569Sphilip		mode = sc->port.link_mode;
734227569Sphilip		ifmr->ifm_active |= sfxge_link_mode[medium_type][mode];
735227569Sphilip		ifmr->ifm_active |= sfxge_port_link_fc_ifm(sc);
736227569Sphilip	}
737227569Sphilip
738278221Sarybchik	SFXGE_ADAPTER_UNLOCK(sc);
739227569Sphilip}
740227569Sphilip
741227569Sphilipstatic int
742227569Sphilipsfxge_media_change(struct ifnet *ifp)
743227569Sphilip{
744227569Sphilip	struct sfxge_softc *sc;
745227569Sphilip	struct ifmedia_entry *ifm;
746227569Sphilip	int rc;
747227569Sphilip
748227569Sphilip	sc = ifp->if_softc;
749227569Sphilip	ifm = sc->media.ifm_cur;
750227569Sphilip
751278221Sarybchik	SFXGE_ADAPTER_LOCK(sc);
752227569Sphilip
753227569Sphilip	if (!SFXGE_RUNNING(sc)) {
754227569Sphilip		rc = 0;
755227569Sphilip		goto out;
756227569Sphilip	}
757227569Sphilip
758227569Sphilip	rc = efx_mac_fcntl_set(sc->enp, sfxge_port_wanted_fc(sc), B_TRUE);
759227569Sphilip	if (rc != 0)
760227569Sphilip		goto out;
761227569Sphilip
762227569Sphilip	rc = efx_phy_adv_cap_set(sc->enp, ifm->ifm_data);
763227569Sphilipout:
764278221Sarybchik	SFXGE_ADAPTER_UNLOCK(sc);
765227569Sphilip
766272325Sgnn	return (rc);
767227569Sphilip}
768227569Sphilip
769227569Sphilipint sfxge_port_ifmedia_init(struct sfxge_softc *sc)
770227569Sphilip{
771227569Sphilip	efx_phy_media_type_t medium_type;
772227569Sphilip	uint32_t cap_mask, mode_cap_mask;
773227569Sphilip	efx_link_mode_t mode;
774227569Sphilip	int mode_ifm, best_mode_ifm = 0;
775227569Sphilip	int rc;
776227569Sphilip
777227569Sphilip	/* We need port state to initialise the ifmedia list. */
778227569Sphilip	if ((rc = efx_nic_init(sc->enp)) != 0)
779227569Sphilip		goto out;
780227569Sphilip	if ((rc = efx_port_init(sc->enp)) != 0)
781227569Sphilip		goto out2;
782227569Sphilip
783227569Sphilip	/*
784227569Sphilip	 * Register ifconfig callbacks for querying and setting the
785227569Sphilip	 * link mode and link status.
786227569Sphilip	 */
787227569Sphilip	ifmedia_init(&sc->media, IFM_IMASK, sfxge_media_change,
788227569Sphilip	    sfxge_media_status);
789227569Sphilip
790227569Sphilip	/*
791227569Sphilip	 * Map firmware medium type and capabilities to ifmedia types.
792227569Sphilip	 * ifmedia does not distinguish between forcing the link mode
793227569Sphilip	 * and disabling auto-negotiation.  1000BASE-T and 10GBASE-T
794227569Sphilip	 * require AN even if only one link mode is enabled, and for
795227569Sphilip	 * 100BASE-TX it is useful even if the link mode is forced.
796227569Sphilip	 * Therefore we never disable auto-negotiation.
797227569Sphilip	 *
798227569Sphilip	 * Also enable and advertise flow control by default.
799227569Sphilip	 */
800227569Sphilip
801227569Sphilip	efx_phy_media_type_get(sc->enp, &medium_type);
802227569Sphilip	efx_phy_adv_cap_get(sc->enp, EFX_PHY_CAP_PERM, &cap_mask);
803227569Sphilip
804227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_10HDX == EFX_PHY_CAP_10HDX + 1);
805227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_10FDX == EFX_PHY_CAP_10FDX + 1);
806227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_100HDX == EFX_PHY_CAP_100HDX + 1);
807227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_100FDX == EFX_PHY_CAP_100FDX + 1);
808227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_1000HDX == EFX_PHY_CAP_1000HDX + 1);
809227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_1000FDX == EFX_PHY_CAP_1000FDX + 1);
810227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_10000FDX == EFX_PHY_CAP_10000FDX + 1);
811227569Sphilip
812227569Sphilip	for (mode = EFX_LINK_10HDX; mode <= EFX_LINK_10000FDX; mode++) {
813227569Sphilip		mode_cap_mask = 1 << (mode - 1);
814227569Sphilip		mode_ifm = sfxge_link_mode[medium_type][mode];
815227569Sphilip
816227569Sphilip		if ((cap_mask & mode_cap_mask) && mode_ifm) {
817227569Sphilip			mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_AN);
818227569Sphilip
819227569Sphilip#ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
820227569Sphilip			/* No flow-control */
821227569Sphilip			ifmedia_add(&sc->media, mode_ifm, mode_cap_mask, NULL);
822227569Sphilip
823227569Sphilip			/* Respond-only.  If using AN, we implicitly
824227569Sphilip			 * offer symmetric as well, but that doesn't
825227569Sphilip			 * mean we *have* to generate pause frames.
826227569Sphilip			 */
827227569Sphilip			mode_cap_mask |= cap_mask & ((1 << EFX_PHY_CAP_PAUSE) |
828227569Sphilip						     (1 << EFX_PHY_CAP_ASYM));
829227569Sphilip			mode_ifm |= IFM_ETH_RXPAUSE;
830227569Sphilip			ifmedia_add(&sc->media, mode_ifm, mode_cap_mask, NULL);
831227569Sphilip
832227569Sphilip			/* Symmetric */
833227569Sphilip			mode_cap_mask &= ~(1 << EFX_PHY_CAP_ASYM);
834227569Sphilip			mode_ifm |= IFM_ETH_TXPAUSE;
835227569Sphilip#else /* !SFXGE_HAVE_PAUSE_MEDIAOPTS */
836227569Sphilip			mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_PAUSE);
837227569Sphilip#endif
838227569Sphilip			ifmedia_add(&sc->media, mode_ifm, mode_cap_mask, NULL);
839227569Sphilip
840227569Sphilip			/* Link modes are numbered in order of speed,
841227569Sphilip			 * so assume the last one available is the best.
842227569Sphilip			 */
843227569Sphilip			best_mode_ifm = mode_ifm;
844227569Sphilip		}
845227569Sphilip	}
846227569Sphilip
847227569Sphilip	if (cap_mask & (1 << EFX_PHY_CAP_AN)) {
848227569Sphilip		/* Add autoselect mode. */
849227569Sphilip		mode_ifm = IFM_ETHER | IFM_AUTO;
850227569Sphilip		ifmedia_add(&sc->media, mode_ifm,
851227569Sphilip			    cap_mask & ~(1 << EFX_PHY_CAP_ASYM), NULL);
852227569Sphilip		best_mode_ifm = mode_ifm;
853227569Sphilip	}
854227569Sphilip
855272325Sgnn	if (best_mode_ifm != 0)
856227569Sphilip		ifmedia_set(&sc->media, best_mode_ifm);
857227569Sphilip
858227569Sphilip	/* Now discard port state until interface is started. */
859227569Sphilip	efx_port_fini(sc->enp);
860227569Sphilipout2:
861227569Sphilip	efx_nic_fini(sc->enp);
862227569Sphilipout:
863272325Sgnn	return (rc);
864227569Sphilip}
865