]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/mips/cavium/octe/ethernet-tx.c
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.git] / sys / mips / cavium / octe / ethernet-tx.c
1 /*************************************************************************
2 Copyright (c) 2003-2007  Cavium Networks (support@cavium.com). All rights
3 reserved.
4
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are
8 met:
9
10     * Redistributions of source code must retain the above copyright
11       notice, this list of conditions and the following disclaimer.
12
13     * Redistributions in binary form must reproduce the above
14       copyright notice, this list of conditions and the following
15       disclaimer in the documentation and/or other materials provided
16       with the distribution.
17
18     * Neither the name of Cavium Networks nor the names of
19       its contributors may be used to endorse or promote products
20       derived from this software without specific prior written
21       permission.
22
23 This Software, including technical data, may be subject to U.S. export  control laws, including the U.S. Export Administration Act and its  associated regulations, and may be subject to export or import  regulations in other countries.
24
25 TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
26 AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
27
28 *************************************************************************/
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/endian.h>
37 #include <sys/kernel.h>
38 #include <sys/mbuf.h>
39 #include <sys/socket.h>
40
41 #include <net/bpf.h>
42 #include <net/ethernet.h>
43 #include <net/if.h>
44
45 #include "wrapper-cvmx-includes.h"
46 #include "ethernet-headers.h"
47
48 /* You can define GET_MBUF_QOS() to override how the mbuf output function
49    determines which output queue is used. The default implementation
50    always uses the base queue for the port. If, for example, you wanted
51    to use the m->priority fieid, define GET_MBUF_QOS as:
52    #define GET_MBUF_QOS(m) ((m)->priority) */
53 #ifndef GET_MBUF_QOS
54     #define GET_MBUF_QOS(m) 0
55 #endif
56
57 extern int pow_send_group;
58
59
60 /**
61  * Packet transmit
62  *
63  * @param m    Packet to send
64  * @param dev    Device info structure
65  * @return Always returns zero
66  */
67 int cvm_oct_xmit(struct mbuf *m, struct ifnet *ifp)
68 {
69         cvmx_pko_command_word0_t    pko_command;
70         cvmx_buf_ptr_t              hw_buffer;
71         uint64_t                    old_scratch;
72         uint64_t                    old_scratch2;
73         int                         dropped;
74         int                         qos;
75         cvm_oct_private_t          *priv = (cvm_oct_private_t *)ifp->if_softc;
76         int32_t in_use;
77         int32_t buffers_to_free;
78         cvmx_wqe_t *work;
79
80         /* Prefetch the private data structure.
81            It is larger that one cache line */
82         CVMX_PREFETCH(priv, 0);
83
84         /* Start off assuming no drop */
85         dropped = 0;
86
87         /* The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to completely
88            remove "qos" in the event neither interface supports multiple queues
89            per port */
90         if ((CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 > 1) ||
91             (CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 > 1)) {
92                 qos = GET_MBUF_QOS(m);
93                 if (qos <= 0)
94                         qos = 0;
95                 else if (qos >= cvmx_pko_get_num_queues(priv->port))
96                         qos = 0;
97         } else
98                 qos = 0;
99
100         if (USE_ASYNC_IOBDMA) {
101                 /* Save scratch in case userspace is using it */
102                 CVMX_SYNCIOBDMA;
103                 old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
104                 old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH+8);
105
106                 /* Assume we're going to be able t osend this packet. Fetch and increment
107                    the number of pending packets for output */
108                 cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH+8, FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
109                 cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, priv->fau+qos*4, 1);
110         }
111
112         /* The CN3XXX series of parts has an errata (GMX-401) which causes the
113            GMX block to hang if a collision occurs towards the end of a
114            <68 byte packet. As a workaround for this, we pad packets to be
115            68 bytes whenever we are in half duplex mode. We don't handle
116            the case of having a small packet but no room to add the padding.
117            The kernel should always give us at least a cache line */
118         if (__predict_false(m->m_pkthdr.len < 64) && OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
119                 cvmx_gmxx_prtx_cfg_t gmx_prt_cfg;
120                 int interface = INTERFACE(priv->port);
121                 int index = INDEX(priv->port);
122
123                 if (interface < 2) {
124                         /* We only need to pad packet in half duplex mode */
125                         gmx_prt_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
126                         if (gmx_prt_cfg.s.duplex == 0) {
127                                 static uint8_t pad[64];
128
129                                 if (!m_append(m, sizeof pad - m->m_pkthdr.len, pad))
130                                         printf("%s: unable to padd small packet.", __func__);
131                         }
132                 }
133         }
134
135         /*
136          * If the packet is not fragmented.
137          */
138         if (m->m_pkthdr.len == m->m_len) {
139                 /* Build the PKO buffer pointer */
140                 hw_buffer.u64 = 0;
141                 hw_buffer.s.addr = cvmx_ptr_to_phys(m->m_data);
142                 hw_buffer.s.pool = 0;
143                 hw_buffer.s.size = m->m_len;
144
145                 /* Build the PKO command */
146                 pko_command.u64 = 0;
147                 pko_command.s.segs = 1;
148
149                 work = NULL;
150         } else {
151                 struct mbuf *n;
152                 unsigned segs;
153                 uint64_t *gp;
154
155                 /*
156                  * The packet is fragmented, we need to send a list of segments
157                  * in memory we borrow from the WQE pool.
158                  */
159                 work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL);
160                 gp = (uint64_t *)work;
161
162                 segs = 0;
163                 for (n = m; n != NULL; n = n->m_next) {
164                         if (segs == CVMX_FPA_WQE_POOL_SIZE / sizeof (uint64_t))
165                                 panic("%s: too many segments in packet; call m_collapse().", __func__);
166
167                         /* Build the PKO buffer pointer */
168                         hw_buffer.u64 = 0;
169                         hw_buffer.s.addr = cvmx_ptr_to_phys(n->m_data);
170                         hw_buffer.s.pool = 0;
171                         hw_buffer.s.size = n->m_len;
172
173                         *gp++ = hw_buffer.u64;
174                         segs++;
175                 }
176
177                 /* Build the PKO buffer gather list pointer */
178                 hw_buffer.u64 = 0;
179                 hw_buffer.s.addr = cvmx_ptr_to_phys(work);
180                 hw_buffer.s.pool = CVMX_FPA_WQE_POOL;
181                 hw_buffer.s.size = segs;
182
183                 /* Build the PKO command */
184                 pko_command.u64 = 0;
185                 pko_command.s.segs = segs;
186                 pko_command.s.gather = 1;
187         }
188
189         /* Finish building the PKO command */
190         pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */
191         pko_command.s.dontfree = 1;
192         pko_command.s.reg0 = priv->fau+qos*4;
193         pko_command.s.reg0 = priv->fau+qos*4;
194         pko_command.s.total_bytes = m->m_pkthdr.len;
195         pko_command.s.size0 = CVMX_FAU_OP_SIZE_32;
196         pko_command.s.subone0 = 1;
197
198         /* Check if we can use the hardware checksumming */
199         if (USE_HW_TCPUDP_CHECKSUM &&
200             (m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) != 0) {
201                 /* Use hardware checksum calc */
202                 pko_command.s.ipoffp1 = ETHER_HDR_LEN + 1;
203         }
204
205         IF_LOCK(&priv->tx_free_queue[qos]);
206         if (USE_ASYNC_IOBDMA) {
207                 /* Get the number of mbufs in use by the hardware */
208                 CVMX_SYNCIOBDMA;
209                 in_use = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
210                 buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH+8);
211         } else {
212                 /* Get the number of mbufs in use by the hardware */
213                 in_use = cvmx_fau_fetch_and_add32(priv->fau+qos*4, 1);
214                 buffers_to_free = cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
215         }
216
217         cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos, CVMX_PKO_LOCK_CMD_QUEUE);
218
219         /* Drop this packet if we have too many already queued to the HW */
220         if (_IF_QFULL(&priv->tx_free_queue[qos])) {
221                 dropped = 1;
222         }
223         /* Send the packet to the output queue */
224         else
225         if (__predict_false(cvmx_pko_send_packet_finish(priv->port, priv->queue + qos, pko_command, hw_buffer, CVMX_PKO_LOCK_CMD_QUEUE))) {
226                 DEBUGPRINT("%s: Failed to send the packet\n", if_name(ifp));
227                 dropped = 1;
228         }
229
230         if (USE_ASYNC_IOBDMA) {
231                 /* Restore the scratch area */
232                 cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
233                 cvmx_scratch_write64(CVMX_SCR_SCRATCH+8, old_scratch2);
234         }
235
236         if (__predict_false(dropped)) {
237                 m_freem(m);
238                 cvmx_fau_atomic_add32(priv->fau+qos*4, -1);
239                 ifp->if_oerrors++;
240         } else {
241                 /* Put this packet on the queue to be freed later */
242                 _IF_ENQUEUE(&priv->tx_free_queue[qos], m);
243
244                 /* Pass it to any BPF listeners.  */
245                 ETHER_BPF_MTAP(ifp, m);
246         }
247         if (work != NULL)
248                 cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
249
250         /* Free mbufs not in use by the hardware */
251         if (_IF_QLEN(&priv->tx_free_queue[qos]) > in_use) {
252                 while (_IF_QLEN(&priv->tx_free_queue[qos]) > in_use) {
253                         _IF_DEQUEUE(&priv->tx_free_queue[qos], m);
254                         m_freem(m);
255                 }
256         }
257         IF_UNLOCK(&priv->tx_free_queue[qos]);
258
259         return dropped;
260 }
261
262
263 /**
264  * Packet transmit to the POW
265  *
266  * @param m    Packet to send
267  * @param dev    Device info structure
268  * @return Always returns zero
269  */
270 int cvm_oct_xmit_pow(struct mbuf *m, struct ifnet *ifp)
271 {
272         cvm_oct_private_t  *priv = (cvm_oct_private_t *)ifp->if_softc;
273         char               *packet_buffer;
274         char               *copy_location;
275
276         /* Get a work queue entry */
277         cvmx_wqe_t *work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL);
278         if (__predict_false(work == NULL)) {
279                 DEBUGPRINT("%s: Failed to allocate a work queue entry\n", if_name(ifp));
280                 ifp->if_oerrors++;
281                 m_freem(m);
282                 return 0;
283         }
284
285         /* Get a packet buffer */
286         packet_buffer = cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL);
287         if (__predict_false(packet_buffer == NULL)) {
288                 DEBUGPRINT("%s: Failed to allocate a packet buffer\n",
289                            if_name(ifp));
290                 cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
291                 ifp->if_oerrors++;
292                 m_freem(m);
293                 return 0;
294         }
295
296         /* Calculate where we need to copy the data to. We need to leave 8 bytes
297            for a next pointer (unused). We also need to include any configure
298            skip. Then we need to align the IP packet src and dest into the same
299            64bit word. The below calculation may add a little extra, but that
300            doesn't hurt */
301         copy_location = packet_buffer + sizeof(uint64_t);
302         copy_location += ((CVMX_HELPER_FIRST_MBUFF_SKIP+7)&0xfff8) + 6;
303
304         /* We have to copy the packet since whoever processes this packet
305            will free it to a hardware pool. We can't use the trick of
306            counting outstanding packets like in cvm_oct_xmit */
307         m_copydata(m, 0, m->m_pkthdr.len, copy_location);
308
309         /* Fill in some of the work queue fields. We may need to add more
310            if the software at the other end needs them */
311 #if 0
312         work->hw_chksum     = m->csum;
313 #endif
314         work->len           = m->m_pkthdr.len;
315         work->ipprt         = priv->port;
316         work->qos           = priv->port & 0x7;
317         work->grp           = pow_send_group;
318         work->tag_type      = CVMX_HELPER_INPUT_TAG_TYPE;
319         work->tag           = pow_send_group; /* FIXME */
320         work->word2.u64     = 0;    /* Default to zero. Sets of zero later are commented out */
321         work->word2.s.bufs  = 1;
322         work->packet_ptr.u64 = 0;
323         work->packet_ptr.s.addr = cvmx_ptr_to_phys(copy_location);
324         work->packet_ptr.s.pool = CVMX_FPA_PACKET_POOL;
325         work->packet_ptr.s.size = CVMX_FPA_PACKET_POOL_SIZE;
326         work->packet_ptr.s.back = (copy_location - packet_buffer)>>7;
327
328         panic("%s: POW transmit not quite implemented yet.", __func__);
329 #if 0
330         if (m->protocol == htons(ETH_P_IP)) {
331                 work->word2.s.ip_offset     = 14;
332                 #if 0
333                 work->word2.s.vlan_valid  = 0; /* FIXME */
334                 work->word2.s.vlan_cfi    = 0; /* FIXME */
335                 work->word2.s.vlan_id     = 0; /* FIXME */
336                 work->word2.s.dec_ipcomp  = 0; /* FIXME */
337                 #endif
338                 work->word2.s.tcp_or_udp    = (ip_hdr(m)->protocol == IP_PROTOCOL_TCP) || (ip_hdr(m)->protocol == IP_PROTOCOL_UDP);
339                 #if 0
340                 work->word2.s.dec_ipsec   = 0; /* FIXME */
341                 work->word2.s.is_v6       = 0; /* We only support IPv4 right now */
342                 work->word2.s.software    = 0; /* Hardware would set to zero */
343                 work->word2.s.L4_error    = 0; /* No error, packet is internal */
344                 #endif
345                 work->word2.s.is_frag       = !((ip_hdr(m)->frag_off == 0) || (ip_hdr(m)->frag_off == 1<<14));
346                 #if 0
347                 work->word2.s.IP_exc      = 0;  /* Assume Linux is sending a good packet */
348                 #endif
349                 work->word2.s.is_bcast      = (m->pkt_type == PACKET_BROADCAST);
350                 work->word2.s.is_mcast      = (m->pkt_type == PACKET_MULTICAST);
351                 #if 0
352                 work->word2.s.not_IP      = 0; /* This is an IP packet */
353                 work->word2.s.rcv_error   = 0; /* No error, packet is internal */
354                 work->word2.s.err_code    = 0; /* No error, packet is internal */
355                 #endif
356
357                 /* When copying the data, include 4 bytes of the ethernet header to
358                    align the same way hardware does */
359                 memcpy(work->packet_data, m->data + 10, sizeof(work->packet_data));
360         } else {
361                 #if 0
362                 work->word2.snoip.vlan_valid  = 0; /* FIXME */
363                 work->word2.snoip.vlan_cfi    = 0; /* FIXME */
364                 work->word2.snoip.vlan_id     = 0; /* FIXME */
365                 work->word2.snoip.software    = 0; /* Hardware would set to zero */
366                 #endif
367                 work->word2.snoip.is_rarp       = m->protocol == htons(ETH_P_RARP);
368                 work->word2.snoip.is_arp        = m->protocol == htons(ETH_P_ARP);
369                 work->word2.snoip.is_bcast      = (m->pkt_type == PACKET_BROADCAST);
370                 work->word2.snoip.is_mcast      = (m->pkt_type == PACKET_MULTICAST);
371                 work->word2.snoip.not_IP        = 1; /* IP was done up above */
372                 #if 0
373                 work->word2.snoip.rcv_error   = 0; /* No error, packet is internal */
374                 work->word2.snoip.err_code    = 0; /* No error, packet is internal */
375                 #endif
376                 memcpy(work->packet_data, m->data, sizeof(work->packet_data));
377         }
378 #endif
379
380         /* Submit the packet to the POW */
381         cvmx_pow_work_submit(work, work->tag, work->tag_type, work->qos, work->grp);
382         ifp->if_opackets++;
383         ifp->if_obytes += m->m_pkthdr.len;
384         m_freem(m);
385         return 0;
386 }
387
388
389 /**
390  * This function frees all mbufs that are currenty queued for TX.
391  *
392  * @param dev    Device being shutdown
393  */
394 void cvm_oct_tx_shutdown(struct ifnet *ifp)
395 {
396         cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
397         int qos;
398
399         for (qos = 0; qos < 16; qos++) {
400                 IF_DRAIN(&priv->tx_free_queue[qos]);
401         }
402 }