extern struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops);
 extern struct Qdisc *qdisc_create_dflt(struct net_device *dev,
                                       struct Qdisc_ops *ops, u32 parentid);
-
-static inline void
-tcf_destroy(struct tcf_proto *tp)
-{
-       tp->ops->destroy(tp);
-       module_put(tp->ops->owner);
-       kfree(tp);
-}
+extern void tcf_destroy(struct tcf_proto *tp);
+extern void tcf_destroy_chain(struct tcf_proto *fl);
 
 static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch,
                                       struct sk_buff_head *list)
 
        return -1;
 }
 
+void tcf_destroy(struct tcf_proto *tp)
+{
+       tp->ops->destroy(tp);
+       module_put(tp->ops->owner);
+       kfree(tp);
+}
+
+void tcf_destroy_chain(struct tcf_proto *fl)
+{
+       struct tcf_proto *tp;
+
+       while ((tp = fl) != NULL) {
+               fl = tp->next;
+               tcf_destroy(tp);
+       }
+}
+EXPORT_SYMBOL(tcf_destroy_chain);
+
 #ifdef CONFIG_PROC_FS
 static int psched_show(struct seq_file *seq, void *v)
 {
 
        return atm_tc_get(sch,classid);
 }
 
-
-static void destroy_filters(struct atm_flow_data *flow)
-{
-       struct tcf_proto *filter;
-
-       while ((filter = flow->filter_list)) {
-               DPRINTK("destroy_filters: destroying filter %p\n",filter);
-               flow->filter_list = filter->next;
-               tcf_destroy(filter);
-       }
-}
-
-
 /*
  * atm_tc_put handles all destructions, including the ones that are explicitly
  * requested (atm_tc_destroy, etc.). The assumption here is that we never drop
        *prev = flow->next;
        DPRINTK("atm_tc_put: qdisc %p\n",flow->q);
        qdisc_destroy(flow->q);
-       destroy_filters(flow);
+       tcf_destroy_chain(flow->filter_list);
        if (flow->sock) {
                DPRINTK("atm_tc_put: f_count %d\n",
                    file_count(flow->sock->file));
        DPRINTK("atm_tc_destroy(sch %p,[qdisc %p])\n",sch,p);
        /* races ? */
        while ((flow = p->flows)) {
-               destroy_filters(flow);
+               tcf_destroy_chain(flow->filter_list);
                if (flow->ref > 1)
                        printk(KERN_ERR "atm_destroy: %p->ref = %d\n",flow,
                            flow->ref);
 
        return 0;
 }
 
-static void cbq_destroy_filters(struct cbq_class *cl)
-{
-       struct tcf_proto *tp;
-
-       while ((tp = cl->filter_list) != NULL) {
-               cl->filter_list = tp->next;
-               tcf_destroy(tp);
-       }
-}
-
 static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl)
 {
        struct cbq_sched_data *q = qdisc_priv(sch);
 
        BUG_TRAP(!cl->filters);
 
-       cbq_destroy_filters(cl);
+       tcf_destroy_chain(cl->filter_list);
        qdisc_destroy(cl->q);
        qdisc_put_rtab(cl->R_tab);
 #ifdef CONFIG_NET_ESTIMATOR
         */
        for (h = 0; h < 16; h++)
                for (cl = q->classes[h]; cl; cl = cl->next)
-                       cbq_destroy_filters(cl);
+                       tcf_destroy_chain(cl->filter_list);
 
        for (h = 0; h < 16; h++) {
                struct cbq_class *next;
 
 static void dsmark_destroy(struct Qdisc *sch)
 {
        struct dsmark_qdisc_data *p = PRIV(sch);
-       struct tcf_proto *tp;
 
        DPRINTK("dsmark_destroy(sch %p,[qdisc %p])\n", sch, p);
 
-       while (p->filter_list) {
-               tp = p->filter_list;
-               p->filter_list = tp->next;
-               tcf_destroy(tp);
-       }
-
+       tcf_destroy_chain(p->filter_list);
        qdisc_destroy(p->q);
        kfree(p->mask);
 }
 
        return 0;
 }
 
-static void
-hfsc_destroy_filters(struct tcf_proto **fl)
-{
-       struct tcf_proto *tp;
-
-       while ((tp = *fl) != NULL) {
-               *fl = tp->next;
-               tcf_destroy(tp);
-       }
-}
-
 static void
 hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl)
 {
        struct hfsc_sched *q = qdisc_priv(sch);
 
-       hfsc_destroy_filters(&cl->filter_list);
+       tcf_destroy_chain(cl->filter_list);
        qdisc_destroy(cl->qdisc);
 #ifdef CONFIG_NET_ESTIMATOR
        gen_kill_estimator(&cl->bstats, &cl->rate_est);
 
        return (unsigned long)cl;
 }
 
-static void htb_destroy_filters(struct tcf_proto **fl)
-{
-       struct tcf_proto *tp;
-
-       while ((tp = *fl) != NULL) {
-               *fl = tp->next;
-               tcf_destroy(tp);
-       }
-}
-
 static inline int htb_parent_last_child(struct htb_class *cl)
 {
        if (!cl->parent)
        qdisc_put_rtab(cl->rate);
        qdisc_put_rtab(cl->ceil);
 
-       htb_destroy_filters(&cl->filter_list);
+       tcf_destroy_chain(cl->filter_list);
 
        while (!list_empty(&cl->children))
                htb_destroy_class(sch, list_entry(cl->children.next,
           and surprisingly it worked in 2.4. But it must precede it
           because filter need its target class alive to be able to call
           unbind_filter on it (without Oops). */
-       htb_destroy_filters(&q->filter_list);
+       tcf_destroy_chain(q->filter_list);
 
        while (!list_empty(&q->root))
                htb_destroy_class(sch, list_entry(q->root.next,
 
 static void ingress_destroy(struct Qdisc *sch)
 {
        struct ingress_qdisc_data *p = PRIV(sch);
-       struct tcf_proto *tp;
 
        DPRINTK("ingress_destroy(sch %p,[qdisc %p])\n", sch, p);
-       while (p->filter_list) {
-               tp = p->filter_list;
-               p->filter_list = tp->next;
-               tcf_destroy(tp);
-       }
+       tcf_destroy_chain(p->filter_list);
 #if 0
 /* for future use */
        qdisc_destroy(p->q);
 
 {
        int prio;
        struct prio_sched_data *q = qdisc_priv(sch);
-       struct tcf_proto *tp;
-
-       while ((tp = q->filter_list) != NULL) {
-               q->filter_list = tp->next;
-               tcf_destroy(tp);
-       }
 
+       tcf_destroy_chain(q->filter_list);
        for (prio=0; prio<q->bands; prio++)
                qdisc_destroy(q->queues[prio]);
 }