]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/hfa/fore_receive.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / sys / dev / hfa / fore_receive.c
1 /*-
2  * ===================================
3  * HARP  |  Host ATM Research Platform
4  * ===================================
5  *
6  * This Host ATM Research Platform ("HARP") file (the "Software") is
7  * made available by Network Computing Services, Inc. ("NetworkCS")
8  * "AS IS".  NetworkCS does not provide maintenance, improvements or
9  * support of any kind.
10  *
11  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
12  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
14  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
15  * In no event shall NetworkCS be responsible for any damages, including
16  * but not limited to consequential damages, arising from or relating to
17  * any use of the Software or related support.
18  *
19  * Copyright 1994-1998 Network Computing Services, Inc.
20  *
21  * Copies of this Software may be made, however, the above copyright
22  * notice must be reproduced on all copies.
23  */
24
25 #include <sys/cdefs.h>
26 __FBSDID("$FreeBSD$");
27
28 /*
29  * FORE Systems 200-Series Adapter Support
30  * ---------------------------------------
31  *
32  * Receive queue management
33  *
34  */
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/socket.h>
39 #include <sys/socketvar.h>
40 #include <sys/syslog.h>
41 #include <vm/vm.h>
42 #include <vm/pmap.h>
43 #include <net/if.h>
44 #include <net/netisr.h>
45 #include <netatm/port.h>
46 #include <netatm/queue.h>
47 #include <netatm/atm.h>
48 #include <netatm/atm_sys.h>
49 #include <netatm/atm_sap.h>
50 #include <netatm/atm_cm.h>
51 #include <netatm/atm_if.h>
52 #include <netatm/atm_vc.h>
53 #include <netatm/atm_stack.h>
54 #include <netatm/atm_pcb.h>
55 #include <netatm/atm_var.h>
56 #include <dev/pci/pcivar.h>
57 #include <dev/hfa/fore.h>
58 #include <dev/hfa/fore_aali.h>
59 #include <dev/hfa/fore_slave.h>
60 #include <dev/hfa/fore_stats.h>
61 #include <dev/hfa/fore_var.h>
62 #include <dev/hfa/fore_include.h>
63
64 #ifndef lint
65 __RCSID("@(#) $FreeBSD$");
66 #endif
67
68
69 /*
70  * Local functions
71  */
72 static void     fore_recv_stack(void *, KBuffer *);
73
74
75 /*
76  * Allocate Receive Queue Data Structures
77  *
78  * Arguments:
79  *      fup             pointer to device unit structure
80  *
81  * Returns:
82  *      0               allocations successful
83  *      else            allocation failed
84  */
85 int
86 fore_recv_allocate(fup)
87         Fore_unit       *fup;
88 {
89         caddr_t         memp;
90         vm_paddr_t      pmemp;
91
92         /*
93          * Allocate non-cacheable memory for receive status words
94          */
95         memp = atm_dev_alloc(sizeof(Q_status) * RECV_QUELEN,
96                         QSTAT_ALIGN, ATM_DEV_NONCACHE);
97         if (memp == NULL) {
98                 return (1);
99         }
100         fup->fu_recv_stat = (Q_status *) memp;
101
102         pmemp = vtophys(fup->fu_recv_stat);
103         if (pmemp == 0) {
104                 return (1);
105         }
106         fup->fu_recv_statd = pmemp;
107
108         /*
109          * Allocate memory for receive descriptors
110          */
111         memp = atm_dev_alloc(sizeof(Recv_descr) * RECV_QUELEN,
112                         RECV_DESCR_ALIGN, 0);
113         if (memp == NULL) {
114                 return (1);
115         }
116         fup->fu_recv_desc = (Recv_descr *) memp;
117
118         pmemp = vtophys(fup->fu_recv_desc);
119         if (pmemp == 0) {
120                 return (1);
121         }
122         fup->fu_recv_descd = pmemp;
123
124         return (0);
125 }
126
127
128 /*
129  * Receive Queue Initialization
130  *
131  * Allocate and initialize the host-resident receive queue structures
132  * and then initialize the CP-resident queue structures.
133  * 
134  * Called at interrupt level.
135  *
136  * Arguments:
137  *      fup             pointer to device unit structure
138  *
139  * Returns:
140  *      none
141  */
142 void
143 fore_recv_initialize(fup)
144         Fore_unit       *fup;
145 {
146         Aali            *aap = fup->fu_aali;
147         Recv_queue      *cqp;
148         H_recv_queue    *hrp;
149         Recv_descr      *rdp;
150         vm_paddr_t      rdp_dma;
151         Q_status        *qsp;
152         vm_paddr_t      qsp_dma;
153         int             i;
154
155         /*
156          * Point to CP-resident receive queue
157          */
158         cqp = (Recv_queue *)(fup->fu_ram + CP_READ(aap->aali_recv_q));
159
160         /*
161          * Point to host-resident receive queue structures
162          */
163         hrp = fup->fu_recv_q;
164         qsp = fup->fu_recv_stat;
165         qsp_dma = fup->fu_recv_statd;
166         rdp = fup->fu_recv_desc;
167         rdp_dma = fup->fu_recv_descd;
168
169         /*
170          * Loop thru all queue entries and do whatever needs doing
171          */
172         for (i = 0; i < RECV_QUELEN; i++) {
173
174                 /*
175                  * Set queue status word to free
176                  */
177                 *qsp = QSTAT_FREE;
178
179                 /*
180                  * Set up host queue entry and link into ring
181                  */
182                 hrp->hrq_cpelem = cqp;
183                 hrp->hrq_status = qsp;
184                 hrp->hrq_descr = rdp;
185                 hrp->hrq_descr_dma = rdp_dma;
186                 if (i == (RECV_QUELEN - 1))
187                         hrp->hrq_next = fup->fu_recv_q;
188                 else
189                         hrp->hrq_next = hrp + 1;
190
191                 /*
192                  * Now let the CP into the game
193                  */
194                 cqp->cq_descr = (CP_dma) CP_WRITE(rdp_dma);
195                 cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
196
197                 /*
198                  * Bump all queue pointers
199                  */
200                 hrp++;
201                 qsp++;
202                 qsp_dma += sizeof(Q_status);
203                 rdp++;
204                 rdp_dma += sizeof(Recv_descr);
205                 cqp++;
206         }
207
208         /*
209          * Initialize queue pointers
210          */
211         fup->fu_recv_head = fup->fu_recv_q;
212
213         return;
214 }
215
216
217 /*
218  * Drain Receive Queue
219  *
220  * This function will process all completed entries at the head of the
221  * receive queue.  The received segments will be linked into a received
222  * PDU buffer chain and it will then be passed up the PDU's VCC stack for 
223  * processing by the next higher protocol layer.
224  *
225  * May be called in interrupt state.
226  * Must be called with interrupts locked out.
227  *
228  * Arguments:
229  *      fup             pointer to device unit structure
230  *
231  * Returns:
232  *      none
233  */
234 void
235 fore_recv_drain(fup)
236         Fore_unit       *fup;
237 {
238         H_recv_queue    *hrp = NULL;
239         Recv_descr      *rdp;
240         Recv_seg_descr  *rsp;
241         Buf_handle      *bhp;
242         Fore_vcc        *fvp;
243         struct vccb     *vcp;
244         KBuffer         *m, *mhead, *mtail;
245         caddr_t         cp;
246         u_long          hdr, nsegs;
247         u_int           seglen, type0;
248         int             i, pdulen, retries = 0, error;
249
250         /* Silence the compiler */
251         mtail = NULL;
252         type0 = 0;
253
254         /*
255          * Process each completed entry
256          */
257 retry:
258         while (*fup->fu_recv_head->hrq_status & QSTAT_COMPLETED) {
259
260                 /*
261                  * Get completed entry's receive descriptor
262                  */
263                 hrp = fup->fu_recv_head;
264                 rdp = hrp->hrq_descr;
265
266 #ifdef VAC
267                 /*
268                  * Cache flush receive descriptor 
269                  */
270                 if (vac) {
271                         vac_flush((addr_t)rdp, sizeof(Recv_descr));
272                 }
273 #endif
274
275                 hdr = rdp->rd_cell_hdr;
276                 nsegs = rdp->rd_nsegs;
277
278                 pdulen = 0;
279                 error = 0;
280                 mhead = NULL;
281
282                 /*
283                  * Locate incoming VCC for this PDU
284                  */
285                 fvp = (Fore_vcc *) atm_dev_vcc_find((Cmn_unit *)fup,
286                         ATM_HDR_GET_VPI(hdr), ATM_HDR_GET_VCI(hdr), VCC_IN);
287
288                 /*
289                  * Check for a receive error
290                  *
291                  * Apparently the receive descriptor itself contains valid 
292                  * information, but the received pdu data is probably bogus.
293                  * We'll arrange for the receive buffer segments to be tossed.
294                  */
295                 if (*hrp->hrq_status & QSTAT_ERROR) {
296
297                         fup->fu_pif.pif_ierrors++;
298                         if (fvp) {
299                                 vcp = fvp->fv_connvc->cvc_vcc;
300                                 vcp->vc_ierrors++;
301                                 if (vcp->vc_nif)
302                                         ANIF2IFP(vcp->vc_nif)->if_ierrors++;
303                         }
304                         ATM_DEBUG1("fore receive error: hdr=0x%lx\n", hdr);
305                         error = 1;
306                 }
307
308                 /*
309                  * Build PDU buffer chain from receive segments
310                  */
311                 for (i = 0, rsp = rdp->rd_seg; i < nsegs; i++, rsp++) {
312
313                         bhp = rsp->rsd_handle;
314                         seglen = rsp->rsd_len;
315
316                         /*
317                          * Remove buffer from our supplied queue and get
318                          * to the underlying buffer
319                          */
320                         switch (bhp->bh_type) {
321
322                         case BHT_S1_SMALL:
323                                 DEQUEUE(bhp, Buf_handle, bh_qelem,
324                                         fup->fu_buf1s_bq);
325                                 fup->fu_buf1s_cnt--;
326                                 m = (KBuffer *) ((caddr_t)bhp - BUF1_SM_HOFF);
327                                 KB_DATASTART(m, cp, caddr_t);
328                                 break;
329
330                         case BHT_S1_LARGE:
331                                 DEQUEUE(bhp, Buf_handle, bh_qelem,
332                                         fup->fu_buf1l_bq);
333                                 fup->fu_buf1l_cnt--;
334                                 m = (KBuffer *) ((caddr_t)bhp - BUF1_LG_HOFF);
335                                 KB_DATASTART(m, cp, caddr_t);
336                                 break;
337
338                         default:
339                                 log(LOG_ERR,
340                                         "fore_recv_drain: bhp=%p type=0x%x\n",
341                                         bhp, bhp->bh_type);
342                                 panic("fore_recv_drain: bad buffer type");
343                         }
344
345                         /*
346                          * Toss any zero-length or receive error buffers 
347                          */
348                         if ((seglen == 0) || error) {
349                                 KB_FREEALL(m);
350                                 continue;
351                         }
352
353                         /*
354                          * Link buffer into chain
355                          */
356                         if (mhead == NULL) {
357                                 type0 = bhp->bh_type;
358                                 KB_LINKHEAD(m, mhead);
359                                 mhead = m;
360                         } else {
361                                 KB_LINK(m, mtail);
362                         }
363                         KB_LEN(m) = seglen;
364                         pdulen += seglen;
365                         mtail = m;
366
367                         /*
368                          * Flush received buffer data
369                          */
370 #ifdef VAC
371                         if (vac) {
372                                 addr_t  dp;
373
374                                 KB_DATASTART(m, dp, addr_t);
375                                 vac_pageflush(dp);
376                         }
377 #endif
378                 }
379
380                 /*
381                  * Make sure we've got a non-null PDU
382                  */
383                 if (mhead == NULL) {
384                         goto free_ent;
385                 }
386
387                 /*
388                  * We only support user data PDUs (for now)
389                  */
390                 if (hdr & ATM_HDR_SET_PT(ATM_PT_NONUSER)) {
391                         KB_FREEALL(mhead);
392                         goto free_ent;
393                 }
394
395                 /*
396                  * Toss the data if there's no VCC
397                  */
398                 if (fvp == NULL) {
399                         fup->fu_stats->st_drv.drv_rv_novcc++;
400                         KB_FREEALL(mhead);
401                         goto free_ent;
402                 }
403
404 #ifdef DIAGNOSTIC
405                 if (atm_dev_print)
406                         atm_dev_pdu_print((Cmn_unit *)fup, (Cmn_vcc *)fvp, 
407                                 mhead, "fore_recv");
408 #endif
409
410                 /*
411                  * Make sure we have our queueing headroom at the front
412                  * of the buffer chain
413                  */
414                 if (type0 != BHT_S1_SMALL) {
415
416                         /*
417                          * Small buffers already have headroom built-in, but
418                          * if CP had to use a large buffer for the first 
419                          * buffer, then we have to allocate a buffer here to
420                          * contain the headroom.
421                          */
422                         fup->fu_stats->st_drv.drv_rv_nosbf++;
423
424                         KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA);
425                         if (m == NULL) {
426                                 fup->fu_stats->st_drv.drv_rv_nomb++;
427                                 KB_FREEALL(mhead);
428                                 goto free_ent;
429                         }
430
431                         /*
432                          * Put new buffer at head of PDU chain
433                          */
434                         KB_LINKHEAD(m, mhead);
435                         KB_LEN(m) = 0;
436                         KB_HEADSET(m, BUF1_SM_DOFF);
437                         mhead = m;
438                 }
439
440                 /*
441                  * It looks like we've got a valid PDU - count it quick!!
442                  */
443                 mhead->m_pkthdr.rcvif = NULL;
444                 mhead->m_pkthdr.csum_flags = 0;
445                 SLIST_INIT(&mhead->m_pkthdr.tags);
446                 KB_PLENSET(mhead, pdulen);
447                 fup->fu_pif.pif_ipdus++;
448                 fup->fu_pif.pif_ibytes += pdulen;
449                 vcp = fvp->fv_connvc->cvc_vcc;
450                 vcp->vc_ipdus++;
451                 vcp->vc_ibytes += pdulen;
452                 if (vcp->vc_nif) {
453                         vcp->vc_nif->nif_ibytes += pdulen;
454                         ANIF2IFP(vcp->vc_nif)->if_ipackets++;
455 #if (defined(BSD) && (BSD >= 199103))
456                         ANIF2IFP(vcp->vc_nif)->if_ibytes += pdulen;
457 #endif
458                 }
459
460                 /*
461                  * The STACK_CALL needs to happen at splnet() in order
462                  * for the stack sequence processing to work.  Schedule an
463                  * interrupt queue callback at splnet() since we are 
464                  * currently at device level.
465                  */
466
467                 /*
468                  * Prepend callback function pointer and token value to buffer.
469                  * We have already guaranteed that the space is available
470                  * in the first buffer.
471                  * Don't count this extra fields in m_pkthdr.len (XXX)
472                  */
473                 mhead->m_data -= sizeof(atm_intr_func_t) + sizeof(void *);
474                 mhead->m_len += sizeof(atm_intr_func_t) + sizeof(void *);
475                 cp = mtod(mhead, caddr_t);
476                 *((atm_intr_func_t *)cp) = fore_recv_stack;
477                 cp += sizeof(atm_intr_func_t);
478                 *((void **)cp) = (void *)fvp;
479
480                 /*
481                  * Schedule callback
482                  */
483                 if (netisr_queue(NETISR_ATM, mhead)) {  /* (0) on success. */
484                         fup->fu_stats->st_drv.drv_rv_ifull++;
485                         goto free_ent;
486                 }
487
488 free_ent:
489                 /*
490                  * Mark this entry free for use and bump head pointer
491                  * to the next entry in the queue
492                  */
493                 *hrp->hrq_status = QSTAT_FREE;
494                 hrp->hrq_cpelem->cq_descr = 
495                         (CP_dma) CP_WRITE((u_long)hrp->hrq_descr_dma);
496                 fup->fu_recv_head = hrp->hrq_next;
497         }
498
499         /*
500          * Nearly all of the interrupts generated by the CP will be due
501          * to PDU reception.  However, we may receive an interrupt before
502          * the CP has completed the status word DMA to host memory.  Thus,
503          * if we haven't processed any PDUs during this interrupt, we will
504          * wait a bit for completed work on the receive queue, rather than 
505          * having to field an extra interrupt very soon.
506          */
507         if (hrp == NULL) {
508                 if (++retries <= FORE_RECV_RETRY) {
509                         DELAY(FORE_RECV_DELAY);
510                         goto retry;
511                 }
512         }
513
514         return;
515 }
516
517
518 /*
519  * Pass Incoming PDU up Stack
520  *
521  * This function is called via the core ATM interrupt queue callback 
522  * set in fore_recv_drain().  It will pass the supplied incoming 
523  * PDU up the incoming VCC's stack.
524  *
525  * Called at splnet.
526  *
527  * Arguments:
528  *      tok             token to identify stack instantiation
529  *      m               pointer to incoming PDU buffer chain
530  *
531  * Returns:
532  *      none
533  */
534 static void
535 fore_recv_stack(tok, m)
536         void            *tok;
537         KBuffer         *m;
538 {
539         Fore_vcc        *fvp = (Fore_vcc *)tok;
540         int             err;
541
542         /*
543          * Send the data up the stack
544          */
545         STACK_CALL(CPCS_UNITDATA_SIG, fvp->fv_upper,
546                 fvp->fv_toku, fvp->fv_connvc, (intptr_t)m, 0, err);
547         if (err)
548                 KB_FREEALL(m);
549
550         return;
551 }
552
553
554 /*
555  * Free Receive Queue Data Structures
556  *
557  * Arguments:
558  *      fup             pointer to device unit structure
559  *
560  * Returns:
561  *      none
562  */
563 void
564 fore_recv_free(fup)
565         Fore_unit       *fup;
566 {
567         /*
568          * We'll just let fore_buf_free() take care of freeing any
569          * buffers sitting on the receive queue (which are also still
570          * on the fu_*_bq queue).
571          */
572         if (fup->fu_flags & CUF_INITED) {
573         }
574
575         /*
576          * Free the status words
577          */
578         if (fup->fu_recv_stat) {
579                 atm_dev_free((volatile void *)fup->fu_recv_stat);
580                 fup->fu_recv_stat = NULL;
581                 fup->fu_recv_statd = 0;
582         }
583
584         /*
585          * Free the receive descriptors
586          */
587         if (fup->fu_recv_desc) {
588                 atm_dev_free(fup->fu_recv_desc);
589                 fup->fu_recv_desc = NULL;
590                 fup->fu_recv_descd = 0;
591         }
592
593         return;
594 }
595