mirror of
https://github.com/torvalds/linux.git
synced 2024-12-16 16:12:52 +00:00
Drivers: net: hyperv: Enable scatter gather I/O
Cleanup the code and enable scatter gather I/O. Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
3ee2f8ce1a
commit
54a7357f7a
@ -140,21 +140,123 @@ static void netvsc_xmit_completion(void *context)
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
static u32 fill_pg_buf(struct page *page, u32 offset, u32 len,
|
||||
struct hv_page_buffer *pb)
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
/* Deal with compund pages by ignoring unused part
|
||||
* of the page.
|
||||
*/
|
||||
page += (offset >> PAGE_SHIFT);
|
||||
offset &= ~PAGE_MASK;
|
||||
|
||||
while (len > 0) {
|
||||
unsigned long bytes;
|
||||
|
||||
bytes = PAGE_SIZE - offset;
|
||||
if (bytes > len)
|
||||
bytes = len;
|
||||
pb[j].pfn = page_to_pfn(page);
|
||||
pb[j].offset = offset;
|
||||
pb[j].len = bytes;
|
||||
|
||||
offset += bytes;
|
||||
len -= bytes;
|
||||
|
||||
if (offset == PAGE_SIZE && len) {
|
||||
page++;
|
||||
offset = 0;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return j + 1;
|
||||
}
|
||||
|
||||
static void init_page_array(void *hdr, u32 len, struct sk_buff *skb,
|
||||
struct hv_page_buffer *pb)
|
||||
{
|
||||
u32 slots_used = 0;
|
||||
char *data = skb->data;
|
||||
int frags = skb_shinfo(skb)->nr_frags;
|
||||
int i;
|
||||
|
||||
/* The packet is laid out thus:
|
||||
* 1. hdr
|
||||
* 2. skb linear data
|
||||
* 3. skb fragment data
|
||||
*/
|
||||
if (hdr != NULL)
|
||||
slots_used += fill_pg_buf(virt_to_page(hdr),
|
||||
offset_in_page(hdr),
|
||||
len, &pb[slots_used]);
|
||||
|
||||
slots_used += fill_pg_buf(virt_to_page(data),
|
||||
offset_in_page(data),
|
||||
skb_headlen(skb), &pb[slots_used]);
|
||||
|
||||
for (i = 0; i < frags; i++) {
|
||||
skb_frag_t *frag = skb_shinfo(skb)->frags + i;
|
||||
|
||||
slots_used += fill_pg_buf(skb_frag_page(frag),
|
||||
frag->page_offset,
|
||||
skb_frag_size(frag), &pb[slots_used]);
|
||||
}
|
||||
}
|
||||
|
||||
static int count_skb_frag_slots(struct sk_buff *skb)
|
||||
{
|
||||
int i, frags = skb_shinfo(skb)->nr_frags;
|
||||
int pages = 0;
|
||||
|
||||
for (i = 0; i < frags; i++) {
|
||||
skb_frag_t *frag = skb_shinfo(skb)->frags + i;
|
||||
unsigned long size = skb_frag_size(frag);
|
||||
unsigned long offset = frag->page_offset;
|
||||
|
||||
/* Skip unused frames from start of page */
|
||||
offset &= ~PAGE_MASK;
|
||||
pages += PFN_UP(offset + size);
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
static int netvsc_get_slots(struct sk_buff *skb)
|
||||
{
|
||||
char *data = skb->data;
|
||||
unsigned int offset = offset_in_page(data);
|
||||
unsigned int len = skb_headlen(skb);
|
||||
int slots;
|
||||
int frag_slots;
|
||||
|
||||
slots = DIV_ROUND_UP(offset + len, PAGE_SIZE);
|
||||
frag_slots = count_skb_frag_slots(skb);
|
||||
return slots + frag_slots;
|
||||
}
|
||||
|
||||
static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
|
||||
{
|
||||
struct net_device_context *net_device_ctx = netdev_priv(net);
|
||||
struct hv_netvsc_packet *packet;
|
||||
int ret;
|
||||
unsigned int i, num_pages, npg_data;
|
||||
unsigned int num_data_pages;
|
||||
|
||||
/* Add multipages for skb->data and additional 2 for RNDIS */
|
||||
npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1)
|
||||
>> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1;
|
||||
num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2;
|
||||
/* We will atmost need two pages to describe the rndis
|
||||
* header. We can only transmit MAX_PAGE_BUFFER_COUNT number
|
||||
* of pages in a single packet.
|
||||
*/
|
||||
num_data_pages = netvsc_get_slots(skb) + 2;
|
||||
if (num_data_pages > MAX_PAGE_BUFFER_COUNT) {
|
||||
netdev_err(net, "Packet too big: %u\n", skb->len);
|
||||
dev_kfree_skb(skb);
|
||||
net->stats.tx_dropped++;
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* Allocate a netvsc packet based on # of frags. */
|
||||
packet = kzalloc(sizeof(struct hv_netvsc_packet) +
|
||||
(num_pages * sizeof(struct hv_page_buffer)) +
|
||||
(num_data_pages * sizeof(struct hv_page_buffer)) +
|
||||
sizeof(struct rndis_message) +
|
||||
NDIS_VLAN_PPI_SIZE, GFP_ATOMIC);
|
||||
if (!packet) {
|
||||
@ -169,44 +271,17 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
|
||||
packet->vlan_tci = skb->vlan_tci;
|
||||
|
||||
packet->extension = (void *)(unsigned long)packet +
|
||||
sizeof(struct hv_netvsc_packet) +
|
||||
(num_pages * sizeof(struct hv_page_buffer));
|
||||
sizeof(struct hv_netvsc_packet) +
|
||||
(num_data_pages * sizeof(struct hv_page_buffer));
|
||||
|
||||
/* If the rndis msg goes beyond 1 page, we will add 1 later */
|
||||
packet->page_buf_cnt = num_pages - 1;
|
||||
packet->page_buf_cnt = num_data_pages - 1;
|
||||
|
||||
/* Initialize it from the skb */
|
||||
packet->total_data_buflen = skb->len;
|
||||
|
||||
/* Start filling in the page buffers starting after RNDIS buffer. */
|
||||
packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
|
||||
packet->page_buf[1].offset
|
||||
= (unsigned long)skb->data & (PAGE_SIZE - 1);
|
||||
if (npg_data == 1)
|
||||
packet->page_buf[1].len = skb_headlen(skb);
|
||||
else
|
||||
packet->page_buf[1].len = PAGE_SIZE
|
||||
- packet->page_buf[1].offset;
|
||||
|
||||
for (i = 2; i <= npg_data; i++) {
|
||||
packet->page_buf[i].pfn = virt_to_phys(skb->data
|
||||
+ PAGE_SIZE * (i-1)) >> PAGE_SHIFT;
|
||||
packet->page_buf[i].offset = 0;
|
||||
packet->page_buf[i].len = PAGE_SIZE;
|
||||
}
|
||||
if (npg_data > 1)
|
||||
packet->page_buf[npg_data].len = (((unsigned long)skb->data
|
||||
+ skb_headlen(skb) - 1) & (PAGE_SIZE - 1)) + 1;
|
||||
|
||||
/* Additional fragments are after SKB data */
|
||||
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
||||
const skb_frag_t *f = &skb_shinfo(skb)->frags[i];
|
||||
|
||||
packet->page_buf[i+npg_data+1].pfn =
|
||||
page_to_pfn(skb_frag_page(f));
|
||||
packet->page_buf[i+npg_data+1].offset = f->page_offset;
|
||||
packet->page_buf[i+npg_data+1].len = skb_frag_size(f);
|
||||
}
|
||||
init_page_array(NULL, 0, skb, &packet->page_buf[1]);
|
||||
|
||||
/* Set the completion routine */
|
||||
packet->completion.send.send_completion = netvsc_xmit_completion;
|
||||
@ -451,8 +526,8 @@ static int netvsc_probe(struct hv_device *dev,
|
||||
net->netdev_ops = &device_ops;
|
||||
|
||||
/* TODO: Add GSO and Checksum offload */
|
||||
net->hw_features = 0;
|
||||
net->features = NETIF_F_HW_VLAN_CTAG_TX;
|
||||
net->hw_features = NETIF_F_SG;
|
||||
net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG;
|
||||
|
||||
SET_ETHTOOL_OPS(net, ðtool_ops);
|
||||
SET_NETDEV_DEV(net, &dev->device);
|
||||
|
Loading…
Reference in New Issue
Block a user