fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / xen / netback / netback.c
index 89a196b..6e2778a 100644 (file)
@@ -79,7 +79,7 @@ static inline unsigned long idx_to_kaddr(unsigned int idx)
 
 #define PKT_PROT_LEN 64
 
-static struct {
+static struct pending_tx_info {
        netif_tx_request_t req;
        netif_t *netif;
 } pending_tx_info[MAX_PENDING_REQS];
@@ -139,12 +139,12 @@ static inline void maybe_schedule_tx_action(void)
 
 /*
  * A gross way of confirming the origin of an skb data page. The slab
- * allocator abuses a field in the page struct to cache the kmem_cache_t ptr.
+ * allocator abuses a field in the page struct to cache the struct kmem_cache ptr.
  */
 static inline int is_xen_skb(struct sk_buff *skb)
 {
-       extern kmem_cache_t *skbuff_cachep;
-       kmem_cache_t *cp = (kmem_cache_t *)virt_to_page(skb->head)->lru.next;
+       extern struct kmem_cache *skbuff_cachep;
+       struct kmem_cache *cp = (struct kmem_cache *)virt_to_page(skb->head)->lru.next;
        return (cp == skbuff_cachep);
 }
 
@@ -265,6 +265,13 @@ static inline int netbk_queue_full(netif_t *netif)
               ((netif->rx.rsp_prod_pvt + NET_RX_RING_SIZE - peek) < needed);
 }
 
+static void tx_queue_callback(unsigned long data)
+{
+       netif_t *netif = (netif_t *)data;
+       if (netif_schedulable(netif->dev))
+               netif_wake_queue(netif->dev);
+}
+
 int netif_be_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        netif_t *netif = netdev_priv(dev);
@@ -272,20 +279,13 @@ int netif_be_start_xmit(struct sk_buff *skb, struct net_device *dev)
        BUG_ON(skb->dev != dev);
 
        /* Drop the packet if the target domain has no receive buffers. */
-       if (unlikely(!netif_running(dev) || !netif_carrier_ok(dev)))
+       if (unlikely(!netif_schedulable(dev) || netbk_queue_full(netif)))
                goto drop;
 
-       if (unlikely(netbk_queue_full(netif))) {
-               /* Not a BUG_ON() -- misbehaving netfront can trigger this. */
-               if (netbk_can_queue(dev))
-                       DPRINTK("Queue full but not stopped!\n");
-               goto drop;
-       }
-
-       /* Copy the packet here if it's destined for a flipping
-          interface but isn't flippable (e.g. extra references to
-          data)
-       */
+       /*
+        * Copy the packet here if it's destined for a flipping interface
+        * but isn't flippable (e.g. extra references to data).
+        */
        if (!netif->copying_receiver && !is_flippable_skb(skb)) {
                struct sk_buff *nskb = netbk_copy_skb(skb);
                if ( unlikely(nskb == NULL) )
@@ -306,8 +306,19 @@ int netif_be_start_xmit(struct sk_buff *skb, struct net_device *dev)
                netif->rx.sring->req_event = netif->rx_req_cons_peek +
                        netbk_max_required_rx_slots(netif);
                mb(); /* request notification /then/ check & stop the queue */
-               if (netbk_queue_full(netif))
+               if (netbk_queue_full(netif)) {
                        netif_stop_queue(dev);
+                       /*
+                        * Schedule 500ms timeout to restart the queue, thus
+                        * ensuring that an inactive queue will be drained.
+                        * Packets will be immediately be dropped until more
+                        * receive buffers become available (see
+                        * netbk_queue_full() check above).
+                        */
+                       netif->tx_queue_timeout.data = (unsigned long)netif;
+                       netif->tx_queue_timeout.function = tx_queue_callback;
+                       __mod_timer(&netif->tx_queue_timeout, jiffies + HZ/2);
+               }
        }
 
        skb_queue_tail(&rx_queue, skb);
@@ -375,14 +386,22 @@ static u16 netbk_gop_frag(netif_t *netif, struct netbk_rx_meta *meta,
                   flipped. */
                meta->copy = 1;
                copy_gop = npo->copy + npo->copy_prod++;
-               copy_gop->source.domid = DOMID_SELF;
+               copy_gop->flags = GNTCOPY_dest_gref;
+               if (PageForeign(page)) {
+                       struct pending_tx_info *src_pend =
+                               &pending_tx_info[page->index];
+                       copy_gop->source.domid = src_pend->netif->domid;
+                       copy_gop->source.u.ref = src_pend->req.gref;
+                       copy_gop->flags |= GNTCOPY_source_gref;
+               } else {
+                       copy_gop->source.domid = DOMID_SELF;
+                       copy_gop->source.u.gmfn = old_mfn;
+               }
                copy_gop->source.offset = offset;
-               copy_gop->source.u.gmfn = old_mfn;
                copy_gop->dest.domid = netif->domid;
                copy_gop->dest.offset = 0;
                copy_gop->dest.u.ref = req->gref;
                copy_gop->len = size;
-               copy_gop->flags = GNTCOPY_dest_gref;
        } else {
                meta->copy = 0;
                if (!xen_feature(XENFEAT_auto_translated_physmap)) {
@@ -476,7 +495,7 @@ static int netbk_check_gop(int nr_frags, domid_t domid,
                        copy_op = npo->copy + npo->copy_cons++;
                        if (copy_op->status != GNTST_okay) {
                                DPRINTK("Bad status %d from copy to DOM%d.\n",
-                                       gop->status, domid);
+                                       copy_op->status, domid);
                                status = NETIF_RSP_ERROR;
                        }
                } else {
@@ -658,7 +677,7 @@ static void net_rx_action(unsigned long unused)
                id = meta[npo.meta_cons].id;
                flags = nr_frags ? NETRXF_more_data : 0;
 
-               if (skb->ip_summed == CHECKSUM_HW) /* local packet? */
+               if (skb->ip_summed == CHECKSUM_PARTIAL) /* local packet? */
                        flags |= NETRXF_csum_blank | NETRXF_data_validated;
                else if (skb->proto_data_valid) /* remote but checksummed? */
                        flags |= NETRXF_data_validated;
@@ -699,6 +718,7 @@ static void net_rx_action(unsigned long unused)
                }
 
                if (netif_queue_stopped(netif->dev) &&
+                   netif_schedulable(netif->dev) &&
                    !netbk_queue_full(netif))
                        netif_wake_queue(netif->dev);
 
@@ -756,8 +776,7 @@ static void add_to_net_schedule_list_tail(netif_t *netif)
 
        spin_lock_irq(&net_schedule_list_lock);
        if (!__on_net_schedule_list(netif) &&
-           likely(netif_running(netif->dev) &&
-                  netif_carrier_ok(netif->dev))) {
+           likely(netif_schedulable(netif->dev))) {
                list_add_tail(&netif->list, &net_schedule_list);
                netif_get(netif);
        }
@@ -796,7 +815,7 @@ void netif_deschedule_work(netif_t *netif)
 
 static void tx_add_credit(netif_t *netif)
 {
-       unsigned long max_burst;
+       unsigned long max_burst, max_credit;
 
        /*
         * Allow a burst big enough to transmit a jumbo packet of up to 128kB.
@@ -806,9 +825,12 @@ static void tx_add_credit(netif_t *netif)
        max_burst = min(max_burst, 131072UL);
        max_burst = max(max_burst, netif->credit_bytes);
 
-       netif->remaining_credit = min(netif->remaining_credit +
-                                     netif->credit_bytes,
-                                     max_burst);
+       /* Take care that adding a new chunk of credit doesn't wrap to zero. */
+       max_credit = netif->remaining_credit + netif->credit_bytes;
+       if (max_credit < netif->remaining_credit)
+               max_credit = ULONG_MAX; /* wrapped: clamp to ULONG_MAX */
+
+       netif->remaining_credit = min(max_credit, max_burst);
 }
 
 static void tx_credit_callback(unsigned long data)
@@ -876,20 +898,28 @@ static void netbk_tx_err(netif_t *netif, netif_tx_request_t *txp, RING_IDX end)
        netif_put(netif);
 }
 
-static int netbk_count_requests(netif_t *netif, netif_tx_request_t *txp,
-                               int work_to_do)
+static int netbk_count_requests(netif_t *netif, netif_tx_request_t *first,
+                               netif_tx_request_t *txp, int work_to_do)
 {
-       netif_tx_request_t *first = txp;
        RING_IDX cons = netif->tx.req_cons;
        int frags = 0;
 
-       while (txp->flags & NETTXF_more_data) {
+       if (!(first->flags & NETTXF_more_data))
+               return 0;
+
+       do {
                if (frags >= work_to_do) {
                        DPRINTK("Need more frags\n");
                        return -frags;
                }
 
-               txp = RING_GET_REQUEST(&netif->tx, cons + frags);
+               if (unlikely(frags >= MAX_SKB_FRAGS)) {
+                       DPRINTK("Too many frags\n");
+                       return -frags;
+               }
+
+               memcpy(txp, RING_GET_REQUEST(&netif->tx, cons + frags),
+                      sizeof(*txp));
                if (txp->size > first->size) {
                        DPRINTK("Frags galore\n");
                        return -frags;
@@ -903,27 +933,25 @@ static int netbk_count_requests(netif_t *netif, netif_tx_request_t *txp,
                                txp->offset, txp->size);
                        return -frags;
                }
-       }
+       } while ((txp++)->flags & NETTXF_more_data);
 
        return frags;
 }
 
 static gnttab_map_grant_ref_t *netbk_get_requests(netif_t *netif,
                                                  struct sk_buff *skb,
+                                                 netif_tx_request_t *txp,
                                                  gnttab_map_grant_ref_t *mop)
 {
        struct skb_shared_info *shinfo = skb_shinfo(skb);
        skb_frag_t *frags = shinfo->frags;
-       netif_tx_request_t *txp;
        unsigned long pending_idx = *((u16 *)skb->data);
-       RING_IDX cons = netif->tx.req_cons;
        int i, start;
 
        /* Skip first skb fragment if it is on same page as header fragment. */
        start = ((unsigned long)shinfo->frags[0].page == pending_idx);
 
-       for (i = start; i < shinfo->nr_frags; i++) {
-               txp = RING_GET_REQUEST(&netif->tx, cons++);
+       for (i = start; i < shinfo->nr_frags; i++, txp++) {
                pending_idx = pending_ring[MASK_PEND_IDX(pending_cons++)];
 
                gnttab_set_map_op(mop++, idx_to_kaddr(pending_idx),
@@ -1037,7 +1065,7 @@ static void netbk_fill_frags(struct sk_buff *skb)
 int netbk_get_extras(netif_t *netif, struct netif_extra_info *extras,
                     int work_to_do)
 {
-       struct netif_extra_info *extra;
+       struct netif_extra_info extra;
        RING_IDX cons = netif->tx.req_cons;
 
        do {
@@ -1046,18 +1074,18 @@ int netbk_get_extras(netif_t *netif, struct netif_extra_info *extras,
                        return -EBADR;
                }
 
-               extra = (struct netif_extra_info *)
-                       RING_GET_REQUEST(&netif->tx, cons);
-               if (unlikely(!extra->type ||
-                            extra->type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
+               memcpy(&extra, RING_GET_REQUEST(&netif->tx, cons),
+                      sizeof(extra));
+               if (unlikely(!extra.type ||
+                            extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
                        netif->tx.req_cons = ++cons;
-                       DPRINTK("Invalid extra type: %d\n", extra->type);
+                       DPRINTK("Invalid extra type: %d\n", extra.type);
                        return -EINVAL;
                }
 
-               memcpy(&extras[extra->type - 1], extra, sizeof(*extra));
+               memcpy(&extras[extra.type - 1], &extra, sizeof(extra));
                netif->tx.req_cons = ++cons;
-       } while (extra->flags & XEN_NETIF_EXTRA_FLAG_MORE);
+       } while (extra.flags & XEN_NETIF_EXTRA_FLAG_MORE);
 
        return work_to_do;
 }
@@ -1092,6 +1120,7 @@ static void net_tx_action(unsigned long unused)
        struct sk_buff *skb;
        netif_t *netif;
        netif_tx_request_t txreq;
+       netif_tx_request_t txfrags[MAX_SKB_FRAGS];
        struct netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX - 1];
        u16 pending_idx;
        RING_IDX i;
@@ -1168,19 +1197,13 @@ static void net_tx_action(unsigned long unused)
                        }
                }
 
-               ret = netbk_count_requests(netif, &txreq, work_to_do);
+               ret = netbk_count_requests(netif, &txreq, txfrags, work_to_do);
                if (unlikely(ret < 0)) {
                        netbk_tx_err(netif, &txreq, i - ret);
                        continue;
                }
                i += ret;
 
-               if (unlikely(ret > MAX_SKB_FRAGS)) {
-                       DPRINTK("Too many frags\n");
-                       netbk_tx_err(netif, &txreq, i);
-                       continue;
-               }
-
                if (unlikely(txreq.size < ETH_HLEN)) {
                        DPRINTK("Bad packet size: %d\n", txreq.size);
                        netbk_tx_err(netif, &txreq, i);
@@ -1249,7 +1272,7 @@ static void net_tx_action(unsigned long unused)
 
                pending_cons++;
 
-               mop = netbk_get_requests(netif, skb, mop);
+               mop = netbk_get_requests(netif, skb, txfrags, mop);
 
                netif->tx.req_cons = i;
                netif_schedule_work(netif);
@@ -1339,17 +1362,18 @@ static void netif_page_release(struct page *page)
 {
        /* Ready for next use. */
        init_page_count(page);
+
        netif_idx_release(page->index);
 }
 
-irqreturn_t netif_be_int(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t netif_be_int(int irq, void *dev_id)
 {
        netif_t *netif = dev_id;
 
        add_to_net_schedule_list_tail(netif);
        maybe_schedule_tx_action();
 
-       if (netif_queue_stopped(netif->dev) && !netbk_queue_full(netif))
+       if (netif_schedulable(netif->dev) && !netbk_queue_full(netif))
                netif_wake_queue(netif->dev);
 
        return IRQ_HANDLED;
@@ -1409,7 +1433,7 @@ static netif_rx_response_t *make_rx_response(netif_t *netif,
 }
 
 #ifdef NETBE_DEBUG_INTERRUPT
-static irqreturn_t netif_be_dbg(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t netif_be_dbg(int irq, void *dev_id)
 {
        struct list_head *ent;
        netif_t *netif;