* needs to be reallocated in a driver.
  * The invariant being skb->truesize subtracted from sk->sk_wmem_alloc
  *
- * Since transmit from skb destructor is forbidden, we use a tasklet
+ * Since transmit from skb destructor is forbidden, we use a BH work item
  * to process all sockets that eventually need to send more skbs.
- * We use one tasklet per cpu, with its own queue of sockets.
+ * We use one work item per cpu, with its own queue of sockets.
  */
-struct tsq_tasklet {
-       struct tasklet_struct   tasklet;
+struct tsq_work {
+       struct work_struct      work;
        struct list_head        head; /* queue of tcp sockets */
 };
-static DEFINE_PER_CPU(struct tsq_tasklet, tsq_tasklet);
+static DEFINE_PER_CPU(struct tsq_work, tsq_work);
 
 static void tcp_tsq_write(struct sock *sk)
 {
        bh_unlock_sock(sk);
 }
 /*
- * One tasklet per cpu tries to send more skbs.
- * We run in tasklet context but need to disable irqs when
+ * One work item per cpu tries to send more skbs.
+ * We run in BH context but need to disable irqs when
  * transferring tsq->head because tcp_wfree() might
  * interrupt us (non NAPI drivers)
  */
-static void tcp_tasklet_func(struct tasklet_struct *t)
+static void tcp_tsq_workfn(struct work_struct *work)
 {
-       struct tsq_tasklet *tsq = from_tasklet(tsq,  t, tasklet);
+       struct tsq_work *tsq = container_of(work, struct tsq_work, work);
        LIST_HEAD(list);
        unsigned long flags;
        struct list_head *q, *n;
 }
 EXPORT_IPV6_MOD(tcp_release_cb);
 
-void __init tcp_tasklet_init(void)
+void __init tcp_tsq_work_init(void)
 {
        int i;
 
        for_each_possible_cpu(i) {
-               struct tsq_tasklet *tsq = &per_cpu(tsq_tasklet, i);
+               struct tsq_work *tsq = &per_cpu(tsq_work, i);
 
                INIT_LIST_HEAD(&tsq->head);
-               tasklet_setup(&tsq->tasklet, tcp_tasklet_func);
+               INIT_WORK(&tsq->work, tcp_tsq_workfn);
        }
 }
 
        struct sock *sk = skb->sk;
        struct tcp_sock *tp = tcp_sk(sk);
        unsigned long flags, nval, oval;
-       struct tsq_tasklet *tsq;
+       struct tsq_work *tsq;
        bool empty;
 
        /* Keep one reference on sk_wmem_alloc.
-        * Will be released by sk_free() from here or tcp_tasklet_func()
+        * Will be released by sk_free() from here or tcp_tsq_workfn()
         */
        WARN_ON(refcount_sub_and_test(skb->truesize - 1, &sk->sk_wmem_alloc));
 
                nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED;
        } while (!try_cmpxchg(&sk->sk_tsq_flags, &oval, nval));
 
-       /* queue this socket to tasklet queue */
+       /* queue this socket to BH workqueue */
        local_irq_save(flags);
-       tsq = this_cpu_ptr(&tsq_tasklet);
+       tsq = this_cpu_ptr(&tsq_work);
        empty = list_empty(&tsq->head);
        list_add(&tp->tsq_node, &tsq->head);
        if (empty)
-               tasklet_schedule(&tsq->tasklet);
+               queue_work(system_bh_wq, &tsq->work);
        local_irq_restore(flags);
        return;
 out:
        if (refcount_read(&sk->sk_wmem_alloc) > limit) {
                /* Always send skb if rtx queue is empty or has one skb.
                 * No need to wait for TX completion to call us back,
-                * after softirq/tasklet schedule.
+                * after softirq schedule.
                 * This helps when TX completions are delayed too much.
                 */
                if (tcp_rtx_queue_empty_or_single_skb(sk))