📄 sch_atm.c
字号:
struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow; DPRINTK("atm_tc_walk(sch %p,[qdisc %p],walker %p)\n",sch,p,walker); if (walker->stop) return; for (flow = p->flows; flow; flow = flow->next) { if (walker->count >= walker->skip) if (walker->fn(sch,(unsigned long) flow,walker) < 0) { walker->stop = 1; break; } walker->count++; }}static struct tcf_proto **atm_tc_find_tcf(struct Qdisc *sch,unsigned long cl){ struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow = (struct atm_flow_data *) cl; DPRINTK("atm_tc_find_tcf(sch %p,[qdisc %p],flow %p)\n",sch,p,flow); return flow ? &flow->filter_list : &p->link.filter_list;}/* --------------------------- Qdisc operations ---------------------------- */static int atm_tc_enqueue(struct sk_buff *skb,struct Qdisc *sch){ struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow = NULL ; /* @@@ */ struct tcf_result res; int result; int ret = NET_XMIT_POLICED; D2PRINTK("atm_tc_enqueue(skb %p,sch %p,[qdisc %p])\n",skb,sch,p); result = TC_POLICE_OK; /* be nice to gcc */ if (TC_H_MAJ(skb->priority) != sch->handle || !(flow = (struct atm_flow_data *) atm_tc_get(sch,skb->priority))) for (flow = p->flows; flow; flow = flow->next) if (flow->filter_list) { result = tc_classify(skb,flow->filter_list, &res); if (result < 0) continue; flow = (struct atm_flow_data *) res.class; if (!flow) flow = lookup_flow(sch,res.classid); break; } if (!flow) flow = &p->link; else { if (flow->vcc) ATM_SKB(skb)->atm_options = flow->vcc->atm_options; /*@@@ looks good ... but it's not supposed to work :-)*/#ifdef CONFIG_NET_CLS_POLICE switch (result) { case TC_POLICE_SHOT: kfree_skb(skb); break; case TC_POLICE_RECLASSIFY: if (flow->excess) flow = flow->excess; else { ATM_SKB(skb)->atm_options |= ATM_ATMOPT_CLP; break; } /* fall through */ case TC_POLICE_OK: /* fall through */ default: break; }#endif } if (#ifdef CONFIG_NET_CLS_POLICE result == TC_POLICE_SHOT ||#endif (ret = flow->q->enqueue(skb,flow->q)) != 0) { sch->stats.drops++; if (flow) flow->stats.drops++; return ret; } sch->stats.bytes += skb->len; sch->stats.packets++; flow->stats.bytes += skb->len; flow->stats.packets++; /* * Okay, this may seem weird. We pretend we've dropped the packet if * it goes via ATM. The reason for this is that the outer qdisc * expects to be able to q->dequeue the packet later on if we return * success at this place. Also, sch->q.qdisc needs to reflect whether * there is a packet egligible for dequeuing or not. Note that the * statistics of the outer qdisc are necessarily wrong because of all * this. There's currently no correct solution for this. */ if (flow == &p->link) { sch->q.qlen++; return 0; } tasklet_schedule(&p->task); return NET_XMIT_BYPASS;}/* * Dequeue packets and send them over ATM. Note that we quite deliberately * avoid checking net_device's flow control here, simply because sch_atm * uses its own channels, which have nothing to do with any CLIP/LANE/or * non-ATM interfaces. */static void sch_atm_dequeue(unsigned long data){ struct Qdisc *sch = (struct Qdisc *) data; struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow; struct sk_buff *skb; D2PRINTK("sch_atm_dequeue(sch %p,[qdisc %p])\n",sch,p); for (flow = p->link.next; flow; flow = flow->next) /* * If traffic is properly shaped, this won't generate nasty * little bursts. Otherwise, it may ... (but that's okay) */ while ((skb = flow->q->dequeue(flow->q))) { if (!atm_may_send(flow->vcc,skb->truesize)) { (void) flow->q->ops->requeue(skb,flow->q); break; } D2PRINTK("atm_tc_deqeueue: sending on class %p\n",flow); /* remove any LL header somebody else has attached */ skb_pull(skb,(char *) skb->nh.iph-(char *) skb->data); if (skb_headroom(skb) < flow->hdr_len) { struct sk_buff *new; new = skb_realloc_headroom(skb,flow->hdr_len); dev_kfree_skb(skb); if (!new) continue; skb = new; } D2PRINTK("sch_atm_dequeue: ip %p, data %p\n", skb->nh.iph,skb->data); ATM_SKB(skb)->vcc = flow->vcc; memcpy(skb_push(skb,flow->hdr_len),flow->hdr, flow->hdr_len); atomic_add(skb->truesize,&flow->vcc->tx_inuse); ATM_SKB(skb)->iovcnt = 0; /* atm.atm_options are already set by atm_tc_enqueue */ (void) flow->vcc->send(flow->vcc,skb); }}static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch){ struct atm_qdisc_data *p = PRIV(sch); struct sk_buff *skb; D2PRINTK("atm_tc_dequeue(sch %p,[qdisc %p])\n",sch,p); tasklet_schedule(&p->task); skb = p->link.q->dequeue(p->link.q); if (skb) sch->q.qlen--; return skb;}static int atm_tc_requeue(struct sk_buff *skb,struct Qdisc *sch){ struct atm_qdisc_data *p = PRIV(sch); int ret; D2PRINTK("atm_tc_requeue(skb %p,sch %p,[qdisc %p])\n",skb,sch,p); ret = p->link.q->ops->requeue(skb,p->link.q); if (!ret) sch->q.qlen++; else { sch->stats.drops++; p->link.stats.drops++; } return ret;}static int atm_tc_drop(struct Qdisc *sch){ struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow; DPRINTK("atm_tc_drop(sch %p,[qdisc %p])\n",sch,p); for (flow = p->flows; flow; flow = flow->next) if (flow->q->ops->drop && flow->q->ops->drop(flow->q)) return 1; return 0;}static int atm_tc_init(struct Qdisc *sch,struct rtattr *opt){ struct atm_qdisc_data *p = PRIV(sch); DPRINTK("atm_tc_init(sch %p,[qdisc %p],opt %p)\n",sch,p,opt); memset(p,0,sizeof(*p)); p->flows = &p->link; if(!(p->link.q = qdisc_create_dflt(sch->dev,&pfifo_qdisc_ops))) p->link.q = &noop_qdisc; DPRINTK("atm_tc_init: link (%p) qdisc %p\n",&p->link,p->link.q); p->link.filter_list = NULL; p->link.vcc = NULL; p->link.sock = NULL; p->link.classid = sch->handle; p->link.ref = 1; p->link.next = NULL; tasklet_init(&p->task,sch_atm_dequeue,(unsigned long) sch); MOD_INC_USE_COUNT; return 0;}static void atm_tc_reset(struct Qdisc *sch){ struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow; DPRINTK("atm_tc_reset(sch %p,[qdisc %p])\n",sch,p); for (flow = p->flows; flow; flow = flow->next) qdisc_reset(flow->q); sch->q.qlen = 0;}static void atm_tc_destroy(struct Qdisc *sch){ struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow; DPRINTK("atm_tc_destroy(sch %p,[qdisc %p])\n",sch,p); /* races ? */ while ((flow = p->flows)) { destroy_filters(flow); if (flow->ref > 1) printk(KERN_ERR "atm_destroy: %p->ref = %d\n",flow, flow->ref); atm_tc_put(sch,(unsigned long) flow); if (p->flows == flow) { printk(KERN_ERR "atm_destroy: putting flow %p didn't " "kill it\n",flow); p->flows = flow->next; /* brute force */ break; } } tasklet_kill(&p->task); MOD_DEC_USE_COUNT;}#ifdef CONFIG_RTNETLINKstatic int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm){ struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow = (struct atm_flow_data *) cl; unsigned char *b = skb->tail; struct rtattr *rta; DPRINTK("atm_tc_dump_class(sch %p,[qdisc %p],flow %p,skb %p,tcm %p)\n", sch,p,flow,skb,tcm); if (!find_flow(p,flow)) return -EINVAL; tcm->tcm_handle = flow->classid; rta = (struct rtattr *) b; RTA_PUT(skb,TCA_OPTIONS,0,NULL); RTA_PUT(skb,TCA_ATM_HDR,flow->hdr_len,flow->hdr); if (flow->vcc) { struct sockaddr_atmpvc pvc; int state; pvc.sap_family = AF_ATMPVC; pvc.sap_addr.itf = flow->vcc->dev ? flow->vcc->dev->number : -1; pvc.sap_addr.vpi = flow->vcc->vpi; pvc.sap_addr.vci = flow->vcc->vci; RTA_PUT(skb,TCA_ATM_ADDR,sizeof(pvc),&pvc); state = ATM_VF2VS(flow->vcc->flags); RTA_PUT(skb,TCA_ATM_STATE,sizeof(state),&state); } if (flow->excess) RTA_PUT(skb,TCA_ATM_EXCESS,sizeof(u32),&flow->classid); else { static u32 zero = 0; RTA_PUT(skb,TCA_ATM_EXCESS,sizeof(zero),&zero); } rta->rta_len = skb->tail-b; return skb->len;rtattr_failure: skb_trim(skb,b-skb->data); return -1;}static int atm_tc_dump(struct Qdisc *sch, struct sk_buff *skb){ return 0;}#endifstatic struct Qdisc_class_ops atm_class_ops ={ atm_tc_graft, /* graft */ atm_tc_leaf, /* leaf */ atm_tc_get, /* get */ atm_tc_put, /* put */ atm_tc_change, /* change */ atm_tc_delete, /* delete */ atm_tc_walk, /* walk */ atm_tc_find_tcf, /* tcf_chain */ atm_tc_bind_filter, /* bind_tcf */ atm_tc_put, /* unbind_tcf */#ifdef CONFIG_RTNETLINK atm_tc_dump_class, /* dump */#endif};struct Qdisc_ops atm_qdisc_ops ={ NULL, /* next */ &atm_class_ops, /* cl_ops */ "atm", sizeof(struct atm_qdisc_data), atm_tc_enqueue, /* enqueue */ atm_tc_dequeue, /* dequeue */ atm_tc_requeue, /* requeue */ atm_tc_drop, /* drop */ atm_tc_init, /* init */ atm_tc_reset, /* reset */ atm_tc_destroy, /* destroy */ NULL, /* change */#ifdef CONFIG_RTNETLINK atm_tc_dump /* dump */#endif};#ifdef MODULEint init_module(void){ return register_qdisc(&atm_qdisc_ops);}void cleanup_module(void) { unregister_qdisc(&atm_qdisc_ops);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -