Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-davinci
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
linux
linux-davinci
Commits
a91eba5b
Commit
a91eba5b
authored
Sep 25, 2009
by
David S. Miller
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
ssh://master.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-for-davem
parents
b8273570
0aea51c3
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
88 additions
and
141 deletions
+88
-141
drivers/net/virtio_net.c
drivers/net/virtio_net.c
+88
-141
No files found.
drivers/net/virtio_net.c
View file @
a91eba5b
/* A
simple
network driver using virtio.
/* A network driver using virtio.
*
* Copyright 2007 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation
*
...
...
@@ -48,19 +48,9 @@ struct virtnet_info
struct
napi_struct
napi
;
unsigned
int
status
;
/* The skb we couldn't send because buffers were full. */
struct
sk_buff
*
last_xmit_skb
;
/* If we need to free in a timer, this is it. */
struct
timer_list
xmit_free_timer
;
/* Number of input buffers, and max we've ever had. */
unsigned
int
num
,
max
;
/* For cleaning up after transmission. */
struct
tasklet_struct
tasklet
;
bool
free_in_tasklet
;
/* I like... big packets and I cannot lie! */
bool
big_packets
;
...
...
@@ -78,9 +68,17 @@ struct virtnet_info
struct
page
*
pages
;
};
static
inline
void
*
skb_vnet_hdr
(
struct
sk_buff
*
skb
)
struct
skb_vnet_hdr
{
union
{
struct
virtio_net_hdr
hdr
;
struct
virtio_net_hdr_mrg_rxbuf
mhdr
;
};
unsigned
int
num_sg
;
};
static
inline
struct
skb_vnet_hdr
*
skb_vnet_hdr
(
struct
sk_buff
*
skb
)
{
return
(
struct
virtio_
net_hdr
*
)
skb
->
cb
;
return
(
struct
skb_v
net_hdr
*
)
skb
->
cb
;
}
static
void
give_a_page
(
struct
virtnet_info
*
vi
,
struct
page
*
page
)
...
...
@@ -119,17 +117,13 @@ static void skb_xmit_done(struct virtqueue *svq)
/* We were probably waiting for more output buffers. */
netif_wake_queue
(
vi
->
dev
);
/* Make sure we re-xmit last_xmit_skb: if there are no more packets
* queued, start_xmit won't be called. */
tasklet_schedule
(
&
vi
->
tasklet
);
}
static
void
receive_skb
(
struct
net_device
*
dev
,
struct
sk_buff
*
skb
,
unsigned
len
)
{
struct
virtnet_info
*
vi
=
netdev_priv
(
dev
);
struct
virtio_
net_hdr
*
hdr
=
skb_vnet_hdr
(
skb
);
struct
skb_v
net_hdr
*
hdr
=
skb_vnet_hdr
(
skb
);
int
err
;
int
i
;
...
...
@@ -140,7 +134,6 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
}
if
(
vi
->
mergeable_rx_bufs
)
{
struct
virtio_net_hdr_mrg_rxbuf
*
mhdr
=
skb_vnet_hdr
(
skb
);
unsigned
int
copy
;
char
*
p
=
page_address
(
skb_shinfo
(
skb
)
->
frags
[
0
].
page
);
...
...
@@ -148,8 +141,8 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
len
=
PAGE_SIZE
;
len
-=
sizeof
(
struct
virtio_net_hdr_mrg_rxbuf
);
memcpy
(
hdr
,
p
,
sizeof
(
*
mhdr
));
p
+=
sizeof
(
*
mhdr
);
memcpy
(
&
hdr
->
mhdr
,
p
,
sizeof
(
hdr
->
mhdr
));
p
+=
sizeof
(
hdr
->
mhdr
);
copy
=
len
;
if
(
copy
>
skb_tailroom
(
skb
))
...
...
@@ -164,13 +157,13 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
skb_shinfo
(
skb
)
->
nr_frags
--
;
}
else
{
skb_shinfo
(
skb
)
->
frags
[
0
].
page_offset
+=
sizeof
(
*
mhdr
)
+
copy
;
sizeof
(
hdr
->
mhdr
)
+
copy
;
skb_shinfo
(
skb
)
->
frags
[
0
].
size
=
len
;
skb
->
data_len
+=
len
;
skb
->
len
+=
len
;
}
while
(
--
mhdr
->
num_buffers
)
{
while
(
--
hdr
->
mhdr
.
num_buffers
)
{
struct
sk_buff
*
nskb
;
i
=
skb_shinfo
(
skb
)
->
nr_frags
;
...
...
@@ -184,7 +177,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
nskb
=
vi
->
rvq
->
vq_ops
->
get_buf
(
vi
->
rvq
,
&
len
);
if
(
!
nskb
)
{
pr_debug
(
"%s: rx error: %d buffers missing
\n
"
,
dev
->
name
,
mhdr
->
num_buffers
);
dev
->
name
,
hdr
->
mhdr
.
num_buffers
);
dev
->
stats
.
rx_length_errors
++
;
goto
drop
;
}
...
...
@@ -205,7 +198,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
skb
->
len
+=
len
;
}
}
else
{
len
-=
sizeof
(
struct
virtio_net_
hdr
);
len
-=
sizeof
(
hdr
->
hdr
);
if
(
len
<=
MAX_PACKET_LEN
)
trim_pages
(
vi
,
skb
);
...
...
@@ -223,9 +216,11 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
dev
->
stats
.
rx_bytes
+=
skb
->
len
;
dev
->
stats
.
rx_packets
++
;
if
(
hdr
->
flags
&
VIRTIO_NET_HDR_F_NEEDS_CSUM
)
{
if
(
hdr
->
hdr
.
flags
&
VIRTIO_NET_HDR_F_NEEDS_CSUM
)
{
pr_debug
(
"Needs csum!
\n
"
);
if
(
!
skb_partial_csum_set
(
skb
,
hdr
->
csum_start
,
hdr
->
csum_offset
))
if
(
!
skb_partial_csum_set
(
skb
,
hdr
->
hdr
.
csum_start
,
hdr
->
hdr
.
csum_offset
))
goto
frame_err
;
}
...
...
@@ -233,9 +228,9 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
pr_debug
(
"Receiving skb proto 0x%04x len %i type %i
\n
"
,
ntohs
(
skb
->
protocol
),
skb
->
len
,
skb
->
pkt_type
);
if
(
hdr
->
gso_type
!=
VIRTIO_NET_HDR_GSO_NONE
)
{
if
(
hdr
->
hdr
.
gso_type
!=
VIRTIO_NET_HDR_GSO_NONE
)
{
pr_debug
(
"GSO!
\n
"
);
switch
(
hdr
->
gso_type
&
~
VIRTIO_NET_HDR_GSO_ECN
)
{
switch
(
hdr
->
hdr
.
gso_type
&
~
VIRTIO_NET_HDR_GSO_ECN
)
{
case
VIRTIO_NET_HDR_GSO_TCPV4
:
skb_shinfo
(
skb
)
->
gso_type
=
SKB_GSO_TCPV4
;
break
;
...
...
@@ -248,14 +243,14 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
default:
if
(
net_ratelimit
())
printk
(
KERN_WARNING
"%s: bad gso type %u.
\n
"
,
dev
->
name
,
hdr
->
gso_type
);
dev
->
name
,
hdr
->
hdr
.
gso_type
);
goto
frame_err
;
}
if
(
hdr
->
gso_type
&
VIRTIO_NET_HDR_GSO_ECN
)
if
(
hdr
->
hdr
.
gso_type
&
VIRTIO_NET_HDR_GSO_ECN
)
skb_shinfo
(
skb
)
->
gso_type
|=
SKB_GSO_TCP_ECN
;
skb_shinfo
(
skb
)
->
gso_size
=
hdr
->
gso_size
;
skb_shinfo
(
skb
)
->
gso_size
=
hdr
->
hdr
.
gso_size
;
if
(
skb_shinfo
(
skb
)
->
gso_size
==
0
)
{
if
(
net_ratelimit
())
printk
(
KERN_WARNING
"%s: zero gso size.
\n
"
,
...
...
@@ -285,8 +280,8 @@ static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp)
bool
oom
=
false
;
sg_init_table
(
sg
,
2
+
MAX_SKB_FRAGS
);
for
(;;)
{
struct
virtio_
net_hdr
*
hdr
;
do
{
struct
skb_v
net_hdr
*
hdr
;
skb
=
netdev_alloc_skb
(
vi
->
dev
,
MAX_PACKET_LEN
+
NET_IP_ALIGN
);
if
(
unlikely
(
!
skb
))
{
...
...
@@ -298,7 +293,7 @@ static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp)
skb_put
(
skb
,
MAX_PACKET_LEN
);
hdr
=
skb_vnet_hdr
(
skb
);
sg_set_buf
(
sg
,
hdr
,
sizeof
(
*
hdr
));
sg_set_buf
(
sg
,
&
hdr
->
hdr
,
sizeof
(
hdr
->
hdr
));
if
(
vi
->
big_packets
)
{
for
(
i
=
0
;
i
<
MAX_SKB_FRAGS
;
i
++
)
{
...
...
@@ -328,7 +323,7 @@ static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp)
break
;
}
vi
->
num
++
;
}
}
while
(
err
>=
num
);
if
(
unlikely
(
vi
->
num
>
vi
->
max
))
vi
->
max
=
vi
->
num
;
vi
->
rvq
->
vq_ops
->
kick
(
vi
->
rvq
);
...
...
@@ -346,7 +341,7 @@ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp)
if
(
!
vi
->
mergeable_rx_bufs
)
return
try_fill_recv_maxbufs
(
vi
,
gfp
);
for
(;;)
{
do
{
skb_frag_t
*
f
;
skb
=
netdev_alloc_skb
(
vi
->
dev
,
GOOD_COPY_LEN
+
NET_IP_ALIGN
);
...
...
@@ -380,7 +375,7 @@ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp)
break
;
}
vi
->
num
++
;
}
}
while
(
err
>
0
);
if
(
unlikely
(
vi
->
num
>
vi
->
max
))
vi
->
max
=
vi
->
num
;
vi
->
rvq
->
vq_ops
->
kick
(
vi
->
rvq
);
...
...
@@ -448,42 +443,26 @@ again:
return
received
;
}
static
void
free_old_xmit_skbs
(
struct
virtnet_info
*
vi
)
static
unsigned
int
free_old_xmit_skbs
(
struct
virtnet_info
*
vi
)
{
struct
sk_buff
*
skb
;
unsigned
int
len
;
unsigned
int
len
,
tot_sgs
=
0
;
while
((
skb
=
vi
->
svq
->
vq_ops
->
get_buf
(
vi
->
svq
,
&
len
))
!=
NULL
)
{
pr_debug
(
"Sent skb %p
\n
"
,
skb
);
__skb_unlink
(
skb
,
&
vi
->
send
);
vi
->
dev
->
stats
.
tx_bytes
+=
skb
->
len
;
vi
->
dev
->
stats
.
tx_packets
++
;
tot_sgs
+=
skb_vnet_hdr
(
skb
)
->
num_sg
;
kfree_skb
(
skb
);
}
}
/* If the virtio transport doesn't always notify us when all in-flight packets
* are consumed, we fall back to using this function on a timer to free them. */
static
void
xmit_free
(
unsigned
long
data
)
{
struct
virtnet_info
*
vi
=
(
void
*
)
data
;
netif_tx_lock
(
vi
->
dev
);
free_old_xmit_skbs
(
vi
);
if
(
!
skb_queue_empty
(
&
vi
->
send
))
mod_timer
(
&
vi
->
xmit_free_timer
,
jiffies
+
(
HZ
/
10
));
netif_tx_unlock
(
vi
->
dev
);
return
tot_sgs
;
}
static
int
xmit_skb
(
struct
virtnet_info
*
vi
,
struct
sk_buff
*
skb
)
{
int
num
,
err
;
struct
scatterlist
sg
[
2
+
MAX_SKB_FRAGS
];
struct
virtio_net_hdr_mrg_rxbuf
*
mhdr
=
skb_vnet_hdr
(
skb
);
struct
virtio_net_hdr
*
hdr
=
skb_vnet_hdr
(
skb
);
struct
skb_vnet_hdr
*
hdr
=
skb_vnet_hdr
(
skb
);
const
unsigned
char
*
dest
=
((
struct
ethhdr
*
)
skb
->
data
)
->
h_dest
;
sg_init_table
(
sg
,
2
+
MAX_SKB_FRAGS
);
...
...
@@ -491,108 +470,89 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
pr_debug
(
"%s: xmit %p %pM
\n
"
,
vi
->
dev
->
name
,
skb
,
dest
);
if
(
skb
->
ip_summed
==
CHECKSUM_PARTIAL
)
{
hdr
->
flags
=
VIRTIO_NET_HDR_F_NEEDS_CSUM
;
hdr
->
csum_start
=
skb
->
csum_start
-
skb_headroom
(
skb
);
hdr
->
csum_offset
=
skb
->
csum_offset
;
hdr
->
hdr
.
flags
=
VIRTIO_NET_HDR_F_NEEDS_CSUM
;
hdr
->
hdr
.
csum_start
=
skb
->
csum_start
-
skb_headroom
(
skb
);
hdr
->
hdr
.
csum_offset
=
skb
->
csum_offset
;
}
else
{
hdr
->
flags
=
0
;
hdr
->
csum_offset
=
hdr
->
csum_start
=
0
;
hdr
->
hdr
.
flags
=
0
;
hdr
->
hdr
.
csum_offset
=
hdr
->
hdr
.
csum_start
=
0
;
}
if
(
skb_is_gso
(
skb
))
{
hdr
->
hdr_len
=
skb_headlen
(
skb
);
hdr
->
gso_size
=
skb_shinfo
(
skb
)
->
gso_size
;
hdr
->
hdr
.
hdr
_len
=
skb_headlen
(
skb
);
hdr
->
hdr
.
gso_size
=
skb_shinfo
(
skb
)
->
gso_size
;
if
(
skb_shinfo
(
skb
)
->
gso_type
&
SKB_GSO_TCPV4
)
hdr
->
gso_type
=
VIRTIO_NET_HDR_GSO_TCPV4
;
hdr
->
hdr
.
gso_type
=
VIRTIO_NET_HDR_GSO_TCPV4
;
else
if
(
skb_shinfo
(
skb
)
->
gso_type
&
SKB_GSO_TCPV6
)
hdr
->
gso_type
=
VIRTIO_NET_HDR_GSO_TCPV6
;
hdr
->
hdr
.
gso_type
=
VIRTIO_NET_HDR_GSO_TCPV6
;
else
if
(
skb_shinfo
(
skb
)
->
gso_type
&
SKB_GSO_UDP
)
hdr
->
gso_type
=
VIRTIO_NET_HDR_GSO_UDP
;
hdr
->
hdr
.
gso_type
=
VIRTIO_NET_HDR_GSO_UDP
;
else
BUG
();
if
(
skb_shinfo
(
skb
)
->
gso_type
&
SKB_GSO_TCP_ECN
)
hdr
->
gso_type
|=
VIRTIO_NET_HDR_GSO_ECN
;
hdr
->
hdr
.
gso_type
|=
VIRTIO_NET_HDR_GSO_ECN
;
}
else
{
hdr
->
gso_type
=
VIRTIO_NET_HDR_GSO_NONE
;
hdr
->
gso_size
=
hdr
->
hdr_len
=
0
;
hdr
->
hdr
.
gso_type
=
VIRTIO_NET_HDR_GSO_NONE
;
hdr
->
hdr
.
gso_size
=
hdr
->
hdr
.
hdr_len
=
0
;
}
mhdr
->
num_buffers
=
0
;
hdr
->
mhdr
.
num_buffers
=
0
;
/* Encode metadata header at front. */
if
(
vi
->
mergeable_rx_bufs
)
sg_set_buf
(
sg
,
mhdr
,
sizeof
(
*
mhdr
));
sg_set_buf
(
sg
,
&
hdr
->
mhdr
,
sizeof
(
hdr
->
mhdr
));
else
sg_set_buf
(
sg
,
hdr
,
sizeof
(
*
hdr
));
num
=
skb_to_sgvec
(
skb
,
sg
+
1
,
0
,
skb
->
len
)
+
1
;
err
=
vi
->
svq
->
vq_ops
->
add_buf
(
vi
->
svq
,
sg
,
num
,
0
,
skb
);
if
(
err
>=
0
&&
!
vi
->
free_in_tasklet
)
mod_timer
(
&
vi
->
xmit_free_timer
,
jiffies
+
(
HZ
/
10
));
sg_set_buf
(
sg
,
&
hdr
->
hdr
,
sizeof
(
hdr
->
hdr
));
return
err
;
}
static
void
xmit_tasklet
(
unsigned
long
data
)
{
struct
virtnet_info
*
vi
=
(
void
*
)
data
;
netif_tx_lock_bh
(
vi
->
dev
);
if
(
vi
->
last_xmit_skb
&&
xmit_skb
(
vi
,
vi
->
last_xmit_skb
)
>=
0
)
{
vi
->
svq
->
vq_ops
->
kick
(
vi
->
svq
);
vi
->
last_xmit_skb
=
NULL
;
}
if
(
vi
->
free_in_tasklet
)
free_old_xmit_skbs
(
vi
);
netif_tx_unlock_bh
(
vi
->
dev
);
hdr
->
num_sg
=
skb_to_sgvec
(
skb
,
sg
+
1
,
0
,
skb
->
len
)
+
1
;
return
vi
->
svq
->
vq_ops
->
add_buf
(
vi
->
svq
,
sg
,
hdr
->
num_sg
,
0
,
skb
);
}
static
netdev_tx_t
start_xmit
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
)
{
struct
virtnet_info
*
vi
=
netdev_priv
(
dev
);
int
capacity
;
again:
/* Free up any pending old buffers before queueing new ones. */
free_old_xmit_skbs
(
vi
);
/* If we has a buffer left over from last time, send it now. */
if
(
unlikely
(
vi
->
last_xmit_skb
)
&&
xmit_skb
(
vi
,
vi
->
last_xmit_skb
)
<
0
)
goto
stop_queue
;
vi
->
last_xmit_skb
=
NULL
;
/* Put new one in send queue and do transmit */
if
(
likely
(
skb
))
{
__skb_queue_head
(
&
vi
->
send
,
skb
);
if
(
xmit_skb
(
vi
,
skb
)
<
0
)
{
vi
->
last_xmit_skb
=
skb
;
skb
=
NULL
;
goto
stop_queue
;
capacity
=
xmit_skb
(
vi
,
skb
);
/* This can happen with OOM and indirect buffers. */
if
(
unlikely
(
capacity
<
0
))
{
netif_stop_queue
(
dev
);
dev_warn
(
&
dev
->
dev
,
"Unexpected full queue
\n
"
);
if
(
unlikely
(
!
vi
->
svq
->
vq_ops
->
enable_cb
(
vi
->
svq
)))
{
vi
->
svq
->
vq_ops
->
disable_cb
(
vi
->
svq
);
netif_start_queue
(
dev
);
goto
again
;
}
return
NETDEV_TX_BUSY
;
}
done:
vi
->
svq
->
vq_ops
->
kick
(
vi
->
svq
);
return
NETDEV_TX_OK
;
/* Don't wait up for transmitted skbs to be freed. */
skb_orphan
(
skb
);
nf_reset
(
skb
);
stop_queue:
pr_debug
(
"%s: virtio not prepared to send
\n
"
,
dev
->
name
);
/* Apparently nice girls don't return TX_BUSY; stop the queue
* before it gets out of hand. Naturally, this wastes entries. */
if
(
capacity
<
2
+
MAX_SKB_FRAGS
)
{
netif_stop_queue
(
dev
);
/* Activate callback for using skbs: if this returns false it
* means some were used in the meantime. */
if
(
unlikely
(
!
vi
->
svq
->
vq_ops
->
enable_cb
(
vi
->
svq
)))
{
vi
->
svq
->
vq_ops
->
disable_cb
(
vi
->
svq
);
/* More just got used, free them then recheck. */
capacity
+=
free_old_xmit_skbs
(
vi
);
if
(
capacity
>=
2
+
MAX_SKB_FRAGS
)
{
netif_start_queue
(
dev
);
goto
again
;
vi
->
svq
->
vq_ops
->
disable_cb
(
vi
->
svq
);
}
}
if
(
skb
)
{
/* Drop this skb: we only queue one. */
vi
->
dev
->
stats
.
tx_dropped
++
;
kfree_skb
(
skb
);
}
goto
done
;
return
NETDEV_TX_OK
;
}
static
int
virtnet_set_mac_address
(
struct
net_device
*
dev
,
void
*
p
)
...
...
@@ -925,10 +885,6 @@ static int virtnet_probe(struct virtio_device *vdev)
vi
->
pages
=
NULL
;
INIT_DELAYED_WORK
(
&
vi
->
refill
,
refill_work
);
/* If they give us a callback when all buffers are done, we don't need
* the timer. */
vi
->
free_in_tasklet
=
virtio_has_feature
(
vdev
,
VIRTIO_F_NOTIFY_ON_EMPTY
);
/* If we can receive ANY GSO packets, we must allocate large ones. */
if
(
virtio_has_feature
(
vdev
,
VIRTIO_NET_F_GUEST_TSO4
)
||
virtio_has_feature
(
vdev
,
VIRTIO_NET_F_GUEST_TSO6
)
...
...
@@ -960,11 +916,6 @@ static int virtnet_probe(struct virtio_device *vdev)
skb_queue_head_init
(
&
vi
->
recv
);
skb_queue_head_init
(
&
vi
->
send
);
tasklet_init
(
&
vi
->
tasklet
,
xmit_tasklet
,
(
unsigned
long
)
vi
);
if
(
!
vi
->
free_in_tasklet
)
setup_timer
(
&
vi
->
xmit_free_timer
,
xmit_free
,
(
unsigned
long
)
vi
);
err
=
register_netdev
(
dev
);
if
(
err
)
{
pr_debug
(
"virtio_net: registering device failed
\n
"
);
...
...
@@ -1005,9 +956,6 @@ static void virtnet_remove(struct virtio_device *vdev)
/* Stop all the virtqueues. */
vdev
->
config
->
reset
(
vdev
);
if
(
!
vi
->
free_in_tasklet
)
del_timer_sync
(
&
vi
->
xmit_free_timer
);
/* Free our skbs in send and recv queues, if any. */
while
((
skb
=
__skb_dequeue
(
&
vi
->
recv
))
!=
NULL
)
{
kfree_skb
(
skb
);
...
...
@@ -1041,7 +989,6 @@ static unsigned int features[] = {
VIRTIO_NET_F_GUEST_ECN
,
VIRTIO_NET_F_GUEST_UFO
,
VIRTIO_NET_F_MRG_RXBUF
,
VIRTIO_NET_F_STATUS
,
VIRTIO_NET_F_CTRL_VQ
,
VIRTIO_NET_F_CTRL_RX
,
VIRTIO_NET_F_CTRL_VLAN
,
VIRTIO_F_NOTIFY_ON_EMPTY
,
};
static
struct
virtio_driver
virtio_net
=
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment