Skip to content

Commit

Permalink
netvsc jumbo MTU frame support is now fully working. MTU as large
Browse files Browse the repository at this point in the history
as 12232 was shown to work in testing, but it has been artificially
limited to 9216 by a define in a header file.  Modified ioctl code
to enforce max MTU when set.  RX code now enforces the configured
MTU on receive packets, dropping packets that are larger.  RX and TX
debugging messages are still in place, but ifdefed out; they will
be removed soon.
  • Loading branch information
sjac committed Jul 12, 2012
1 parent 3354798 commit 0af26c4
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 25 deletions.
25 changes: 23 additions & 2 deletions sys/dev/hyperv/netvsc/hv_net_vsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,17 @@ hv_nv_on_send(struct hv_device *device, netvsc_packet *pkt)
return (ENODEV);
}

#ifdef DEBUG_NETVSC_TX
printf("TX: dat %d len %d pbc %d, {%d %d 0x%lx} {%d %d 0x%lx} {%d %d 0x%lx}\n",
pkt->is_data_pkt, pkt->tot_data_buf_len, pkt->page_buf_count,
pkt->page_buffers[0].length, pkt->page_buffers[0].offset,
pkt->page_buffers[0].pfn,
pkt->page_buffers[1].length, pkt->page_buffers[1].offset,
pkt->page_buffers[1].pfn,
pkt->page_buffers[2].length, pkt->page_buffers[2].offset,
pkt->page_buffers[2].pfn);
#endif

send_msg.hdr.msg_type = nvsp_msg_1_type_send_rndis_pkt;
if (pkt->is_data_pkt) {
/* 0 is RMC_DATA */
Expand Down Expand Up @@ -986,6 +997,18 @@ hv_nv_on_receive(struct hv_device *device, hv_vm_packet_descriptor *pkt)
* check for and handle page crossings has been removed.
*/

#ifdef DEBUG_NETVSC_RX
printf("*RX: dat %d len %d pbc %d {%d %d 0x%lx} {%d %d 0x%lx} {%d %d 0x%lx}\n",
net_vsc_pkt->is_data_pkt, net_vsc_pkt->tot_data_buf_len,
net_vsc_pkt->page_buf_count,
net_vsc_pkt->page_buffers[0].length, net_vsc_pkt->page_buffers[0].offset,
net_vsc_pkt->page_buffers[0].pfn,
net_vsc_pkt->page_buffers[1].length, net_vsc_pkt->page_buffers[1].offset,
net_vsc_pkt->page_buffers[1].pfn,
net_vsc_pkt->page_buffers[2].length, net_vsc_pkt->page_buffers[2].offset,
net_vsc_pkt->page_buffers[2].pfn);
#endif

/*
* Pass it to the upper layer. The receive completion call
* has been moved into this function.
Expand Down Expand Up @@ -1105,7 +1128,6 @@ hv_nv_on_channel_callback(void *context)
int ret = 0;

packet = malloc(net_pkt_size * sizeof(uint8_t), M_DEVBUF, M_NOWAIT);

if (!packet) {
return;
}
Expand Down Expand Up @@ -1133,7 +1155,6 @@ hv_nv_on_channel_callback(void *context)
default:
break;
}

} else {
break;
}
Expand Down
10 changes: 8 additions & 2 deletions sys/dev/hyperv/netvsc/hv_net_vsc.h
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,13 @@ typedef struct nvsp_msg_ {
/* Preallocated receive packets */
#define NETVSC_RECEIVE_PACKETLIST_COUNT 256

/*
* Maximum MTU we permit to be configured for a netvsc interface.
* When the code was developed, a max MTU of 12232 was tested and
* proven to work. 9K is a reasonable maximum for an Ethernet.
*/
#define NETVSC_MAX_CONFIGURABLE_MTU (9 * 1024)

/*
* Data types
*/
Expand Down Expand Up @@ -970,8 +977,7 @@ extern int hv_promisc_mode;

extern void netvsc_linkstatus_callback(struct hv_device *device_obj,
uint32_t status);
extern int netvsc_recv_callback(struct hv_device *device_obj,
netvsc_packet *packet);
extern int netvsc_recv(struct hv_device *device_obj, netvsc_packet *packet);
extern void netvsc_xmit_completion(void *context);

extern void hv_nv_on_receive_completion(void *context);
Expand Down
159 changes: 144 additions & 15 deletions sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ netvsc_xmit_completion(void *context)
}

/*
* Start a transmit
* Start a transmit of one or more packets
*/
static int
hn_start_locked(struct ifnet *ifp)
Expand Down Expand Up @@ -375,8 +375,10 @@ hn_start_locked(struct ifnet *ifp)
*/
num_frags += HV_RF_NUM_TX_RESERVED_PAGE_BUFS;

/* If exceeds # page_buffers in netvsc_packet */
if (num_frags > NETVSC_PACKET_MAXPAGE) {
/* Exceeds # page_buffers in netvsc_packet */
m_freem(m);

return (EINVAL);
}

Expand All @@ -395,6 +397,8 @@ hn_start_locked(struct ifnet *ifp)
sizeof(netvsc_packet) + sizeof(rndis_filter_packet),
M_DEVBUF, M_ZERO | M_NOWAIT);
if (buf == NULL) {
m_freem(m);

return (ENOMEM);
}

Expand Down Expand Up @@ -432,6 +436,17 @@ hn_start_locked(struct ifnet *ifp)
}
}

#ifdef DEBUG_NETVSC_TX
printf("ST: dat %d len %d pbc %d, {%d %d 0x%lx} {%d %d 0x%lx} {%d %d 0x%lx}\n",
packet->is_data_pkt, packet->tot_data_buf_len, packet->page_buf_count,
packet->page_buffers[0].length, packet->page_buffers[0].offset,
packet->page_buffers[0].pfn,
packet->page_buffers[1].length, packet->page_buffers[1].offset,
packet->page_buffers[1].pfn,
packet->page_buffers[2].length, packet->page_buffers[2].offset,
packet->page_buffers[2].pfn);
#endif

retry_send:
/* Set the completion routine */
packet->compl.send.on_send_completion = netvsc_xmit_completion;
Expand Down Expand Up @@ -492,17 +507,76 @@ netvsc_linkstatus_callback(struct hv_device *device_obj, uint32_t status)
}

/*
* RX Callback. Called when we receive a data packet from the "wire" on the
* Append the specified data to the indicated mbuf chain,
* Extend the mbuf chain if the new data does not fit in
* existing space.
*
* This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c.
* There should be an equivalent in the kernel mbuf code,
* but there does not appear to be one yet.
*
* Differs from m_append() in that additional mbufs are
* allocated with cluster size MJUMPAGESIZE, and filled
* accordingly.
*
* Return 1 if able to complete the job; otherwise 0.
*/
static int
hv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
{
struct mbuf *m, *n;
int remainder, space;

for (m = m0; m->m_next != NULL; m = m->m_next)
;
remainder = len;
space = M_TRAILINGSPACE(m);
if (space > 0) {
/*
* Copy into available space.
*/
if (space > remainder)
space = remainder;
bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
m->m_len += space;
cp += space;
remainder -= space;
}
while (remainder > 0) {
/*
* Allocate a new mbuf; could check space
* and allocate a cluster instead.
*/
n = m_getjcl(M_DONTWAIT, m->m_type, 0, MJUMPAGESIZE);
if (n == NULL)
break;
n->m_len = min(MJUMPAGESIZE, remainder);
bcopy(cp, mtod(n, caddr_t), n->m_len);
cp += n->m_len;
remainder -= n->m_len;
m->m_next = n;
m = n;
}
if (m0->m_flags & M_PKTHDR)
m0->m_pkthdr.len += len - remainder;

return (remainder == 0);
}


/*
* Called when we receive a data packet from the "wire" on the
* specified device
*
* Fixme: This is no longer used as a callback
* Note: This is no longer used as a callback
*/
int
netvsc_recv_callback(struct hv_device *device_ctx, netvsc_packet *packet)
netvsc_recv(struct hv_device *device_ctx, netvsc_packet *packet)
{
hn_softc_t *sc = (hn_softc_t *)device_get_softc(device_ctx->device);
struct mbuf *m_new;
struct ifnet *ifp = sc->hn_ifp;
int size;
int i;

if (sc == NULL) {
Expand All @@ -514,20 +588,50 @@ netvsc_recv_callback(struct hv_device *device_ctx, netvsc_packet *packet)
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
return (0);
}
if (packet->tot_data_buf_len > MCLBYTES) {

/*
* Bail out if packet contains more data than configured MTU.
*/
if (packet->tot_data_buf_len > (ifp->if_mtu + ETHER_HDR_LEN)) {
return (0);
}

MGETHDR(m_new, M_DONTWAIT, MT_DATA);
if (m_new == NULL) {
return (0);
/*
* Get an mbuf with a cluster. For packets 2K or less,
* get a standard 2K cluster. For anything larger, get a
* 4K cluster. Any buffers larger than 4K can cause problems
* if looped around to the Hyper-V TX channel, so avoid them.
*/
size = MCLBYTES;
if (packet->tot_data_buf_len > MCLBYTES) {
/* 4096 */
size = MJUMPAGESIZE;
}
MCLGET(m_new, M_DONTWAIT);
if ((m_new->m_flags & M_EXT) == 0) {
m_freem(m_new);
m_new = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, size);
if (m_new == NULL) {
return (0);
}

#ifdef DEBUG_NETVSC_RX
printf("++RC: dat %d len %d pbc %d {%d %d 0x%lx} {%d %d 0x%lx} {%d %d 0x%lx}\n",
packet->is_data_pkt, packet->tot_data_buf_len, packet->page_buf_count,
packet->page_buffers[0].length, packet->page_buffers[0].offset,
packet->page_buffers[0].pfn,
packet->page_buffers[1].length, packet->page_buffers[1].offset,
packet->page_buffers[1].pfn,
packet->page_buffers[2].length, packet->page_buffers[2].offset,
packet->page_buffers[2].pfn);
#endif

/*
* Remove trailing junk from RX data buffer.
* Fixme: This will not work for multiple Hyper-V RX buffers.
* Fortunately, the channel gathers all RX data into one buffer.
*
* L2 frame length, with L2 header, not including CRC
*/
packet->page_buffers[0].length = packet->tot_data_buf_len;

/*
* Copy the received packet to one or more mbufs.
* The copy is required since the memory pointed to by netvsc_packet
Expand All @@ -538,14 +642,32 @@ netvsc_recv_callback(struct hv_device *device_ctx, netvsc_packet *packet)
uint8_t *vaddr = (uint8_t *)
(packet->page_buffers[i].pfn << PAGE_SHIFT);

m_append(m_new, packet->page_buffers[i].length,
hv_m_append(m_new, packet->page_buffers[i].length,
vaddr + packet->page_buffers[i].offset);
}

m_new->m_pkthdr.len = m_new->m_len = packet->tot_data_buf_len -
ETHER_CRC_LEN;
m_new->m_pkthdr.rcvif = ifp;

#ifdef DEBUG_NETVSC_RX
{
struct mbuf *m;
int num_frags = 0;
int len = 0;

printf("++++RC: l1 %d l2 %d: ",
packet->page_buffers[0].length, m_new->m_pkthdr.len);

for (m = m_new; m != NULL; m = m->m_next) {
printf(" %d", m->m_len);
if (m->m_len != 0) {
num_frags++;
len += m->m_len;
}
}
printf(": len %d nf %d clen %d\n", m_new->m_len, num_frags, len);
}
#endif

/*
* Note: Moved RX completion back to hv_nv_on_receive() so all
* messages (not just data messages) will trigger a response.
Expand Down Expand Up @@ -583,6 +705,11 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)

SN_LOCK(sc);

if (ifr->ifr_mtu > NETVSC_MAX_CONFIGURABLE_MTU) {
error = EINVAL;
SN_UNLOCK(sc);
break;
}
/* Obtain and record requested MTU */
ifp->if_mtu = ifr->ifr_mtu;

Expand All @@ -593,10 +720,12 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
*/
error = hv_rf_on_device_remove(hn_dev, HV_RF_NV_RETAIN_CHANNEL);
if (error) {
SN_UNLOCK(sc);
break;
}
error = hv_rf_on_device_add(hn_dev, &device_info);
if (error) {
SN_UNLOCK(sc);
break;
}

Expand Down
16 changes: 10 additions & 6 deletions sys/dev/hyperv/netvsc/hv_rndis_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,17 +270,18 @@ hv_rf_receive_data(rndis_device *device, rndis_msg *message, netvsc_packet *pkt)
* netvsc packet (ie tot_data_buf_len != message_length)
*/

/* Remove the rndis header and pass it back up the stack */
/* Remove rndis header, then pass data packet up the stack */
data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;

/* The L2 frame length, including CRC, which must be added. */
pkt->tot_data_buf_len = rndis_pkt->data_length + ETHER_CRC_LEN;
/* L2 frame length, with L2 header, not including CRC */
pkt->tot_data_buf_len = rndis_pkt->data_length;
pkt->page_buffers[0].offset += data_offset;
/* Buffer length now L2 frame length plus trailing junk */
pkt->page_buffers[0].length -= data_offset;

pkt->is_data_pkt = TRUE;

netvsc_recv_callback(device->net_dev->dev, pkt);
netvsc_recv(device->net_dev->dev, pkt);
}

/*
Expand Down Expand Up @@ -531,8 +532,11 @@ hv_rf_init_device(rndis_device *device)
init = &request->request_msg.msg.init_request;
init->major_version = RNDIS_MAJOR_VERSION;
init->minor_version = RNDIS_MINOR_VERSION;
/* Fixme: Use 1536 - rounded ethernet frame size */
/* Fixme: Magic number */
/*
* Per the RNDIS document, this should be set to the max MTU
* plus the header size. However, 2048 works fine, so leaving
* it as is.
*/
init->max_xfer_size = 2048;

device->state = RNDIS_DEV_INITIALIZING;
Expand Down

0 comments on commit 0af26c4

Please sign in to comment.