You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1170 lines
29 KiB
Plaintext
1170 lines
29 KiB
Plaintext
/*********************************************************
|
|
* Copyright (C) 1998-2013 VMware, Inc. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation version 2 and no later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*********************************************************/
|
|
|
|
#include "driver-config.h"
|
|
|
|
#define EXPORT_SYMTAB
|
|
|
|
#define __KERNEL_SYSCALLS__
|
|
|
|
#include <linux/file.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/if_ether.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/sockios.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/version.h>
|
|
#include <linux/wait.h>
|
|
|
|
#include <net/checksum.h>
|
|
#include <net/sock.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include "vnetInt.h"
|
|
#include "compat_skbuff.h"
|
|
#include "vmnetInt.h"
|
|
#include "vm_atomic.h"
|
|
#include "vm_assert.h"
|
|
#include "monitorAction_exported.h"
|
|
|
|
typedef struct VNetUserIFStats {
|
|
unsigned read;
|
|
unsigned written;
|
|
unsigned queued;
|
|
unsigned droppedDown;
|
|
unsigned droppedMismatch;
|
|
unsigned droppedOverflow;
|
|
unsigned droppedLargePacket;
|
|
} VNetUserIFStats;
|
|
|
|
typedef struct VNetUserIF {
|
|
VNetPort port;
|
|
struct sk_buff_head packetQueue;
|
|
Atomic_uint32 *pollPtr;
|
|
MonitorActionIntr *actionIntr;
|
|
uint32 pollMask;
|
|
MonitorIdemAction actionID;
|
|
uint32* recvClusterCount;
|
|
wait_queue_head_t waitQueue;
|
|
struct page* actPage;
|
|
struct page* pollPage;
|
|
struct page* recvClusterPage;
|
|
VNetUserIFStats stats;
|
|
VNetEvent_Sender *eventSender;
|
|
} VNetUserIF;
|
|
|
|
static void VNetUserIfUnsetupNotify(VNetUserIF *userIf);
|
|
static int VNetUserIfSetupNotify(VNetUserIF *userIf, VNet_Notify *vn);
|
|
static int VNetUserIfSetUplinkState(VNetPort *port, uint8 linkUp);
|
|
extern unsigned int vnet_max_qlen;
|
|
|
|
#if COMPAT_LINUX_VERSION_CHECK_LT(3, 2, 0)
|
|
# define compat_kmap(page) kmap(page)
|
|
# define compat_kunmap(page) kunmap(page)
|
|
#else
|
|
# define compat_kmap(page) kmap((page).p)
|
|
# define compat_kunmap(page) kunmap((page).p)
|
|
#endif
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* UserifLockPage --
|
|
*
|
|
* Lock in core the physical page associated to a valid virtual
|
|
* address.
|
|
*
|
|
* Results:
|
|
* The page structure on success
|
|
* NULL on failure: memory pressure. Retry later
|
|
*
|
|
* Side effects:
|
|
* Loads page into memory
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE struct page *
|
|
UserifLockPage(VA addr) // IN
|
|
{
|
|
struct page *page = NULL;
|
|
int retval;
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)
|
|
unsigned int flags = FOLL_WRITE; // Write only
|
|
#endif
|
|
|
|
down_read(¤t->mm->mmap_sem);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)
|
|
retval = get_user_pages_remote(current, current->mm, addr,
|
|
1, flags, &page, NULL);
|
|
#else
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
|
|
retval = get_user_pages_remote(current, current->mm, addr,
|
|
1, 1, 0, &page, NULL);
|
|
#else
|
|
retval = get_user_pages(current, current->mm, addr,
|
|
1, 1, 0, &page, NULL);
|
|
#endif
|
|
#endif
|
|
up_read(¤t->mm->mmap_sem);
|
|
|
|
if (retval != 1) {
|
|
return NULL;
|
|
}
|
|
|
|
return page;
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfMapUint32Ptr --
|
|
*
|
|
* Maps a portion of user-space memory into the kernel.
|
|
*
|
|
* Results:
|
|
* 0 on success
|
|
* < 0 on failure: the actual value determines the type of failure
|
|
*
|
|
* Side effects:
|
|
* Might sleep.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE int
|
|
VNetUserIfMapPtr(VA uAddr, // IN: pointer to user memory
|
|
size_t size, // IN: size of data
|
|
struct page **p, // OUT: locked page
|
|
void **ptr) // OUT: kernel mapped pointer
|
|
{
|
|
if (!access_ok(VERIFY_WRITE, (void *)uAddr, size) ||
|
|
(((uAddr + size - 1) & ~(PAGE_SIZE - 1)) !=
|
|
(uAddr & ~(PAGE_SIZE - 1)))) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*p = UserifLockPage(uAddr);
|
|
if (*p == NULL) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
*ptr = (uint8 *)kmap(*p) + (uAddr & (PAGE_SIZE - 1));
|
|
return 0;
|
|
}
|
|
|
|
static INLINE int
|
|
VNetUserIfMapUint32Ptr(VA uAddr, // IN: pointer to user memory
|
|
struct page **p, // OUT: locked page
|
|
uint32 **ptr) // OUT: kernel mapped pointer
|
|
{
|
|
return VNetUserIfMapPtr(uAddr, sizeof **ptr, p, (void **)ptr);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfSetupNotify --
|
|
*
|
|
* Sets up notification by filling in pollPtr, actPtr, and recvClusterCount
|
|
* fields.
|
|
*
|
|
* Results:
|
|
* 0 on success
|
|
* < 0 on failure: the actual value determines the type of failure
|
|
*
|
|
* Side effects:
|
|
* Fields pollPtr, actPtr, recvClusterCount, pollPage, actPage, and
|
|
* recvClusterPage are filled in VNetUserIf structure.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE int
|
|
VNetUserIfSetupNotify(VNetUserIF *userIf, // IN
|
|
VNet_Notify *vn) // IN
|
|
{
|
|
unsigned long flags;
|
|
struct sk_buff_head *q = &userIf->packetQueue;
|
|
uint32 *pollPtr;
|
|
MonitorActionIntr *actionIntr;
|
|
uint32 *recvClusterCount;
|
|
struct page *pollPage = NULL;
|
|
struct page *actPage = NULL;
|
|
struct page *recvClusterPage = NULL;
|
|
int retval;
|
|
|
|
if (userIf->pollPtr || userIf->actionIntr || userIf->recvClusterCount) {
|
|
LOG(0, (KERN_DEBUG "vmnet: Notification mechanism already active\n"));
|
|
return -EBUSY;
|
|
}
|
|
|
|
if ((retval = VNetUserIfMapUint32Ptr((VA)vn->pollPtr, &pollPage,
|
|
&pollPtr)) < 0) {
|
|
return retval;
|
|
}
|
|
|
|
/* Atomic operations require proper alignment */
|
|
if ((uintptr_t)pollPtr & (sizeof *pollPtr - 1)) {
|
|
LOG(0, (KERN_DEBUG "vmnet: Incorrect notify alignment\n"));
|
|
retval = -EFAULT;
|
|
goto error_free;
|
|
}
|
|
|
|
if ((retval = VNetUserIfMapPtr((VA)vn->actPtr, sizeof *actionIntr,
|
|
&actPage,
|
|
(void **)&actionIntr)) < 0) {
|
|
goto error_free;
|
|
}
|
|
|
|
if ((retval = VNetUserIfMapUint32Ptr((VA)vn->recvClusterPtr,
|
|
&recvClusterPage,
|
|
&recvClusterCount)) < 0) {
|
|
goto error_free;
|
|
}
|
|
|
|
spin_lock_irqsave(&q->lock, flags);
|
|
if (userIf->pollPtr || userIf->actionIntr || userIf->recvClusterCount) {
|
|
spin_unlock_irqrestore(&q->lock, flags);
|
|
retval = -EBUSY;
|
|
LOG(0, (KERN_DEBUG "vmnet: Notification mechanism already active\n"));
|
|
goto error_free;
|
|
}
|
|
|
|
userIf->pollPtr = (Atomic_uint32 *)pollPtr;
|
|
userIf->pollPage = pollPage;
|
|
userIf->actionIntr = actionIntr;
|
|
userIf->actPage = actPage;
|
|
userIf->recvClusterCount = recvClusterCount;
|
|
userIf->recvClusterPage = recvClusterPage;
|
|
userIf->pollMask = vn->pollMask;
|
|
userIf->actionID = vn->actionID;
|
|
spin_unlock_irqrestore(&q->lock, flags);
|
|
return 0;
|
|
|
|
error_free:
|
|
if (pollPage) {
|
|
kunmap(pollPage);
|
|
put_page(pollPage);
|
|
}
|
|
if (actPage) {
|
|
kunmap(actPage);
|
|
put_page(actPage);
|
|
}
|
|
if (recvClusterPage) {
|
|
kunmap(recvClusterPage);
|
|
put_page(recvClusterPage);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfUnsetupNotify --
|
|
*
|
|
* Destroys permanent mapping for notify structure provided by user.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Fields pollPtr, actPtr, recvClusterCount, etc. in VNetUserIf
|
|
* structure are cleared.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static void
|
|
VNetUserIfUnsetupNotify(VNetUserIF *userIf) // IN
|
|
{
|
|
unsigned long flags;
|
|
struct page *pollPage = userIf->pollPage;
|
|
struct page *actPage = userIf->actPage;
|
|
struct page *recvClusterPage = userIf->recvClusterPage;
|
|
|
|
struct sk_buff_head *q = &userIf->packetQueue;
|
|
|
|
spin_lock_irqsave(&q->lock, flags);
|
|
userIf->pollPtr = NULL;
|
|
userIf->pollPage = NULL;
|
|
userIf->actionIntr = NULL;
|
|
userIf->actPage = NULL;
|
|
userIf->recvClusterCount = NULL;
|
|
userIf->recvClusterPage = NULL;
|
|
userIf->pollMask = 0;
|
|
userIf->actionID = -1;
|
|
spin_unlock_irqrestore(&q->lock, flags);
|
|
|
|
/* Release */
|
|
if (pollPage) {
|
|
kunmap(pollPage);
|
|
put_page(pollPage);
|
|
}
|
|
if (actPage) {
|
|
kunmap(actPage);
|
|
put_page(actPage);
|
|
}
|
|
if (recvClusterPage) {
|
|
kunmap(recvClusterPage);
|
|
put_page(recvClusterPage);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfFree --
|
|
*
|
|
* Free the user interface port.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static void
|
|
VNetUserIfFree(VNetJack *this) // IN
|
|
{
|
|
VNetUserIF *userIf = (VNetUserIF*)this;
|
|
struct sk_buff *skb;
|
|
|
|
for (;;) {
|
|
skb = skb_dequeue(&userIf->packetQueue);
|
|
if (skb == NULL) {
|
|
break;
|
|
}
|
|
dev_kfree_skb(skb);
|
|
}
|
|
|
|
if (userIf->pollPtr) {
|
|
VNetUserIfUnsetupNotify(userIf);
|
|
}
|
|
|
|
if (userIf->eventSender) {
|
|
VNetEvent_DestroySender(userIf->eventSender);
|
|
}
|
|
|
|
if (this->procEntry) {
|
|
VNetProc_RemoveEntry(this->procEntry);
|
|
}
|
|
|
|
kfree(userIf);
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfReceive --
|
|
*
|
|
* This jack is receiving a packet. Take appropriate action.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Frees skb.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static void
|
|
VNetUserIfReceive(VNetJack *this, // IN
|
|
struct sk_buff *skb) // IN
|
|
{
|
|
VNetUserIF *userIf = (VNetUserIF*)this->private;
|
|
uint8 *dest = SKB_2_DESTMAC(skb);
|
|
unsigned long flags;
|
|
|
|
if (!UP_AND_RUNNING(userIf->port.flags)) {
|
|
userIf->stats.droppedDown++;
|
|
goto drop_packet;
|
|
}
|
|
|
|
if (!VNetPacketMatch(dest,
|
|
userIf->port.paddr,
|
|
(const uint8 *)userIf->port.exactFilter,
|
|
userIf->port.exactFilterLen,
|
|
userIf->port.ladrf,
|
|
userIf->port.flags)) {
|
|
userIf->stats.droppedMismatch++;
|
|
goto drop_packet;
|
|
}
|
|
|
|
if (skb_queue_len(&userIf->packetQueue) >= vnet_max_qlen) {
|
|
userIf->stats.droppedOverflow++;
|
|
goto drop_packet;
|
|
}
|
|
|
|
if (skb->len > ETHER_MAX_QUEUED_PACKET) {
|
|
userIf->stats.droppedLargePacket++;
|
|
goto drop_packet;
|
|
}
|
|
|
|
userIf->stats.queued++;
|
|
|
|
spin_lock_irqsave(&userIf->packetQueue.lock, flags);
|
|
/*
|
|
* __skb_dequeue_tail does not take any locks so must be used with
|
|
* appropriate locks held only.
|
|
*/
|
|
__skb_queue_tail(&userIf->packetQueue, skb);
|
|
if (userIf->pollPtr) {
|
|
Atomic_Or(userIf->pollPtr, userIf->pollMask);
|
|
if (skb_queue_len(&userIf->packetQueue) >= (*userIf->recvClusterCount)) {
|
|
MonitorAction_SetBits(userIf->actionIntr, userIf->actionID);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&userIf->packetQueue.lock, flags);
|
|
|
|
wake_up(&userIf->waitQueue);
|
|
return;
|
|
|
|
drop_packet:
|
|
dev_kfree_skb(skb);
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfProcRead --
|
|
*
|
|
* Callback for read operation on this userif entry in vnets proc fs.
|
|
*
|
|
* Results:
|
|
* Length of read operation.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
VNetUserIfProcRead(char *page, // IN/OUT: buffer to write into
|
|
char **start, // OUT: 0 if file < 4k, else offset into
|
|
// page
|
|
off_t off, // IN: offset of read into the file
|
|
int count, // IN: maximum number of bytes to read
|
|
int *eof, // OUT: TRUE if there is nothing more to
|
|
// read
|
|
void *data) // IN: client data - not used
|
|
{
|
|
VNetUserIF *userIf = (VNetUserIF*)data;
|
|
int len = 0;
|
|
|
|
if (!userIf) {
|
|
return len;
|
|
}
|
|
|
|
len += VNetPrintPort(&userIf->port, page+len);
|
|
|
|
len += sprintf(page+len, "read %u written %u queued %u ",
|
|
userIf->stats.read,
|
|
userIf->stats.written,
|
|
userIf->stats.queued);
|
|
|
|
len += sprintf(page+len,
|
|
"dropped.down %u dropped.mismatch %u "
|
|
"dropped.overflow %u dropped.largePacket %u",
|
|
userIf->stats.droppedDown,
|
|
userIf->stats.droppedMismatch,
|
|
userIf->stats.droppedOverflow,
|
|
userIf->stats.droppedLargePacket);
|
|
|
|
len += sprintf(page+len, "\n");
|
|
|
|
*start = 0;
|
|
*eof = 1;
|
|
return len;
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetCopyDatagram --
|
|
*
|
|
* Copy part of datagram to userspace.
|
|
*
|
|
* Results:
|
|
* zero on success,
|
|
* -EFAULT if buffer is an invalid area
|
|
*
|
|
* Side effects:
|
|
* Data copied to the buffer.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
VNetCopyDatagram(const struct sk_buff *skb, // IN: skb to copy
|
|
char *buf, // OUT: where to copy data
|
|
int len) // IN: length
|
|
{
|
|
struct iovec iov = {
|
|
.iov_base = buf,
|
|
.iov_len = len,
|
|
};
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
|
|
return skb_copy_datagram_iovec(skb, 0, &iov, len);
|
|
#else
|
|
struct iov_iter ioviter;
|
|
|
|
iov_iter_init(&ioviter, READ, &iov, 1, len);
|
|
return skb_copy_datagram_iter(skb, 0, &ioviter, len);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetCsumCopyDatagram --
|
|
*
|
|
* Copy part of datagram to userspace doing checksum at same time.
|
|
*
|
|
* Do not mark this function INLINE, it is recursive! With all gcc's
|
|
* released up to now (<= gcc-3.3.1) inlining this function just
|
|
* consumes 120 more bytes of code and goes completely mad on
|
|
* register allocation, storing almost everything in the memory.
|
|
*
|
|
* Results:
|
|
* folded checksum (non-negative value) on success,
|
|
* -EINVAL if offset is too big,
|
|
* -EFAULT if buffer is an invalid area
|
|
*
|
|
* Side effects:
|
|
* Data copied to the buffer.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
VNetCsumCopyDatagram(const struct sk_buff *skb, // IN: skb to copy
|
|
unsigned int offset, // IN: how many bytes skip
|
|
char *buf) // OUT: where to copy data
|
|
{
|
|
unsigned int csum;
|
|
int err = 0;
|
|
int len = skb_headlen(skb) - offset;
|
|
char *curr = buf;
|
|
const skb_frag_t *frag;
|
|
|
|
/*
|
|
* Something bad happened. We skip only up to skb->nh.raw, and skb->nh.raw
|
|
* must be in the header, otherwise we are in the big troubles.
|
|
*/
|
|
if (len < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
csum = csum_and_copy_to_user(skb->data + offset, curr, len, 0, &err);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
curr += len;
|
|
|
|
for (frag = skb_shinfo(skb)->frags;
|
|
frag != skb_shinfo(skb)->frags + skb_shinfo(skb)->nr_frags;
|
|
frag++) {
|
|
if (frag->size > 0) {
|
|
unsigned int tmpCsum;
|
|
const void *vaddr;
|
|
|
|
vaddr = compat_kmap(frag->page);
|
|
tmpCsum = csum_and_copy_to_user(vaddr + frag->page_offset,
|
|
curr, frag->size, 0, &err);
|
|
compat_kunmap(frag->page);
|
|
|
|
if (err) {
|
|
return err;
|
|
}
|
|
csum = csum_block_add(csum, tmpCsum, curr - buf);
|
|
curr += frag->size;
|
|
}
|
|
}
|
|
|
|
for (skb = skb_shinfo(skb)->frag_list; skb != NULL; skb = skb->next) {
|
|
int tmpCsum;
|
|
|
|
tmpCsum = VNetCsumCopyDatagram(skb, 0, curr);
|
|
if (tmpCsum < 0) {
|
|
return tmpCsum;
|
|
}
|
|
/* Folded checksum must be inverted before we can use it */
|
|
csum = csum_block_add(csum, tmpCsum ^ 0xFFFF, curr - buf);
|
|
curr += skb->len;
|
|
}
|
|
return csum_fold(csum);
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetCopyDatagramToUser --
|
|
*
|
|
* Copy complete datagram to the user space. Fill correct checksum
|
|
* into the copied datagram if nobody did it yet.
|
|
*
|
|
* Results:
|
|
* On success byte count, on failure -EFAULT.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE_SINGLE_CALLER int
|
|
VNetCopyDatagramToUser(const struct sk_buff *skb, // IN
|
|
char *buf, // OUT
|
|
size_t count) // IN
|
|
{
|
|
if (count > skb->len) {
|
|
count = skb->len;
|
|
}
|
|
/*
|
|
* If truncation occurs, we do not bother with checksumming - caller cannot
|
|
* verify checksum anyway in such case, and copy without checksum is
|
|
* faster.
|
|
*/
|
|
if (skb->pkt_type == PACKET_OUTGOING && /* Packet must be outgoing */
|
|
skb->ip_summed == VM_TX_CHECKSUM_PARTIAL && /* Without checksum */
|
|
compat_skb_network_header_len(skb) && /* We must know where header is */
|
|
skb->len == count) { /* No truncation may occur */
|
|
size_t skl;
|
|
int csum;
|
|
u_int16_t csum16;
|
|
|
|
skl = compat_skb_csum_start(skb);
|
|
if (VNetCopyDatagram(skb, buf, skl)) {
|
|
return -EFAULT;
|
|
}
|
|
csum = VNetCsumCopyDatagram(skb, skl, buf + skl);
|
|
if (csum < 0) {
|
|
return csum;
|
|
}
|
|
csum16 = csum;
|
|
if (copy_to_user(buf + skl + compat_skb_csum_offset(skb),
|
|
&csum16, sizeof csum16)) {
|
|
return -EFAULT;
|
|
}
|
|
} else {
|
|
if (VNetCopyDatagram(skb, buf, count)) {
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfRead --
|
|
*
|
|
* The virtual network's read file operation. Reads the next pending
|
|
* packet for this network connection.
|
|
*
|
|
* Results:
|
|
* On success the len of the packet received,
|
|
* else if no packet waiting and nonblocking 0,
|
|
* else -errno.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
VNetUserIfRead(VNetPort *port, // IN
|
|
struct file *filp, // IN
|
|
char *buf, // OUT
|
|
size_t count) // IN
|
|
{
|
|
VNetUserIF *userIf = (VNetUserIF*)port->jack.private;
|
|
struct sk_buff *skb;
|
|
int ret;
|
|
unsigned long flags;
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
|
|
add_wait_queue(&userIf->waitQueue, &wait);
|
|
for (;;) {
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
skb = skb_peek(&userIf->packetQueue);
|
|
if (skb && (skb->len > count)) {
|
|
skb = NULL;
|
|
ret = -EMSGSIZE;
|
|
break;
|
|
}
|
|
ret = -EAGAIN;
|
|
|
|
spin_lock_irqsave(&userIf->packetQueue.lock, flags);
|
|
/*
|
|
* __skb_dequeue does not take any locks so must be used with
|
|
* appropriate locks held only.
|
|
*/
|
|
skb = __skb_dequeue(&userIf->packetQueue);
|
|
if (userIf->pollPtr) {
|
|
if (!skb) {
|
|
/* List empty */
|
|
Atomic_And(userIf->pollPtr, ~userIf->pollMask);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&userIf->packetQueue.lock, flags);
|
|
|
|
if (skb != NULL || filp->f_flags & O_NONBLOCK) {
|
|
break;
|
|
}
|
|
ret = -EINTR;
|
|
if (signal_pending(current)) {
|
|
break;
|
|
}
|
|
schedule();
|
|
}
|
|
__set_current_state(TASK_RUNNING);
|
|
remove_wait_queue(&userIf->waitQueue, &wait);
|
|
if (! skb) {
|
|
return ret;
|
|
}
|
|
|
|
userIf->stats.read++;
|
|
|
|
count = VNetCopyDatagramToUser(skb, buf, count);
|
|
dev_kfree_skb(skb);
|
|
return count;
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfWrite --
|
|
*
|
|
* The virtual network's write file operation. Send the raw packet
|
|
* to the network.
|
|
*
|
|
* Results:
|
|
* On success the count of bytes written else errno.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
VNetUserIfWrite(VNetPort *port, // IN
|
|
struct file *filp, // IN
|
|
const char *buf, // IN
|
|
size_t count) // IN
|
|
{
|
|
VNetUserIF *userIf = (VNetUserIF*)port->jack.private;
|
|
struct sk_buff *skb;
|
|
|
|
/*
|
|
* Check size
|
|
*/
|
|
|
|
if (count < sizeof (struct ethhdr) ||
|
|
count > ETHER_MAX_QUEUED_PACKET) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Required to enforce the downWhenAddrMismatch policy in the MAC
|
|
* layer. --hpreg
|
|
*/
|
|
if (!UP_AND_RUNNING(userIf->port.flags)) {
|
|
userIf->stats.droppedDown++;
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
* Allocate an sk_buff.
|
|
*/
|
|
|
|
skb = dev_alloc_skb(count + 7);
|
|
if (skb == NULL) {
|
|
// XXX obey O_NONBLOCK?
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
skb_reserve(skb, 2);
|
|
|
|
/*
|
|
* Copy the data and send it.
|
|
*/
|
|
|
|
userIf->stats.written++;
|
|
if (copy_from_user(skb_put(skb, count), buf, count)) {
|
|
dev_kfree_skb(skb);
|
|
return -EFAULT;
|
|
}
|
|
|
|
VNetSend(&userIf->port.jack, skb);
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfIoctl --
|
|
*
|
|
* XXX
|
|
*
|
|
* Results:
|
|
* 0 on success
|
|
* -errno on failure
|
|
*
|
|
* Side effects:
|
|
* None
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
VNetUserIfIoctl(VNetPort *port, // IN
|
|
struct file *filp, // IN
|
|
unsigned int iocmd, // IN
|
|
unsigned long ioarg) // IN or OUT depending on iocmd
|
|
{
|
|
VNetUserIF *userIf = (VNetUserIF*)port->jack.private;
|
|
|
|
switch (iocmd) {
|
|
case SIOCSETNOTIFY:
|
|
return -EINVAL;
|
|
case SIOCSETNOTIFY2:
|
|
#ifdef VMX86_SERVER
|
|
/*
|
|
* This ioctl always return failure on ESX since we cannot map pages into
|
|
* the console os that are from the VMKernel address space which was the
|
|
* only case we used this.
|
|
*/
|
|
return -EINVAL;
|
|
#else // VMX86_SERVER
|
|
/*
|
|
* ORs pollMask into the integer pointed to by ptr if pending packet. Is
|
|
* cleared when all packets are drained.
|
|
*/
|
|
{
|
|
int retval;
|
|
VNet_Notify vn;
|
|
|
|
if (copy_from_user(&vn, (void *)ioarg, sizeof vn)) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
ASSERT_ON_COMPILE(VNET_NOTIFY_VERSION == 5);
|
|
ASSERT_ON_COMPILE(ACTION_EXPORTED_VERSION == 2);
|
|
if (vn.version != VNET_NOTIFY_VERSION ||
|
|
vn.actionVersion != ACTION_EXPORTED_VERSION ||
|
|
vn.actionID / ACTION_WORD_SIZE >= ACTION_NUM_WORDS) {
|
|
return -ENOTTY;
|
|
}
|
|
|
|
retval = VNetUserIfSetupNotify(userIf, &vn);
|
|
if (retval < 0) {
|
|
return retval;
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif // VMX86_SERVER
|
|
case SIOCUNSETNOTIFY:
|
|
if (!userIf->pollPtr) {
|
|
/* This should always happen on ESX. */
|
|
return -EINVAL;
|
|
}
|
|
VNetUserIfUnsetupNotify(userIf);
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
|
/*
|
|
* Drain queue when interface is no longer active. We drain the queue to
|
|
* avoid having old packets delivered to the guest when reneabled.
|
|
*/
|
|
|
|
if (!UP_AND_RUNNING(userIf->port.flags)) {
|
|
struct sk_buff *skb;
|
|
unsigned long flags;
|
|
struct sk_buff_head *q = &userIf->packetQueue;
|
|
|
|
while ((skb = skb_dequeue(q)) != NULL) {
|
|
dev_kfree_skb(skb);
|
|
}
|
|
|
|
spin_lock_irqsave(&q->lock, flags);
|
|
if (userIf->pollPtr) {
|
|
if (skb_queue_empty(q)) {
|
|
/*
|
|
* Clear the pending bit as no packets are pending at this
|
|
* point.
|
|
*/
|
|
Atomic_And(userIf->pollPtr, ~userIf->pollMask);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&q->lock, flags);
|
|
}
|
|
break;
|
|
case SIOCINJECTLINKSTATE:
|
|
{
|
|
uint8 linkUpFromUser;
|
|
if (copy_from_user(&linkUpFromUser, (void *)ioarg,
|
|
sizeof linkUpFromUser)) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (linkUpFromUser != 0 && linkUpFromUser != 1) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return VNetUserIfSetUplinkState(port, linkUpFromUser);
|
|
}
|
|
break;
|
|
default:
|
|
return -ENOIOCTLCMD;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfPoll --
|
|
*
|
|
* The virtual network's file poll operation.
|
|
*
|
|
* Results:
|
|
* Return POLLIN if success, else sleep and return 0.
|
|
* FIXME: Should not we always return POLLOUT?
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
VNetUserIfPoll(VNetPort *port, // IN
|
|
struct file *filp, // IN
|
|
poll_table *wait) // IN
|
|
{
|
|
VNetUserIF *userIf = (VNetUserIF*)port->jack.private;
|
|
|
|
poll_wait(filp, &userIf->waitQueue, wait);
|
|
if (!skb_queue_empty(&userIf->packetQueue)) {
|
|
return POLLIN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetUserIfSetUplinkState --
|
|
*
|
|
* Sends link state change event.
|
|
*
|
|
* Results:
|
|
* 0 on success, errno on failure.
|
|
*
|
|
* Side effects:
|
|
* Link state event is sent to all the event listeners
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
VNetUserIfSetUplinkState(VNetPort *port, uint8 linkUp)
|
|
{
|
|
VNetUserIF *userIf;
|
|
VNetJack *hubJack;
|
|
VNet_LinkStateEvent event;
|
|
int retval;
|
|
|
|
userIf = (VNetUserIF *)port->jack.private;
|
|
hubJack = port->jack.peer;
|
|
|
|
if (port->jack.state == FALSE || hubJack == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (userIf->eventSender == NULL) {
|
|
/* create event sender */
|
|
retval = VNetHub_CreateSender(hubJack, &userIf->eventSender);
|
|
if (retval != 0) {
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
event.header.size = sizeof event;
|
|
retval = VNetEvent_GetSenderId(userIf->eventSender, &event.header.senderId);
|
|
if (retval != 0) {
|
|
LOG(1, (KERN_NOTICE "userif-%d: can't send link state event, "
|
|
"getSenderId failed (%d)\n", userIf->port.id, retval));
|
|
return retval;
|
|
}
|
|
event.header.eventId = 0;
|
|
event.header.classSet = VNET_EVENT_CLASS_UPLINK;
|
|
event.header.type = VNET_EVENT_TYPE_LINK_STATE;
|
|
/*
|
|
* XXX kind of a hack, vmx will coalesce linkup/down if they come from the
|
|
* same adapter.
|
|
*/
|
|
event.adapter = linkUp;
|
|
event.up = linkUp;
|
|
retval = VNetEvent_Send(userIf->eventSender, &event.header);
|
|
if (retval != 0) {
|
|
LOG(1, (KERN_NOTICE "userif-%d: can't send link state event, send "
|
|
"failed (%d)\n", userIf->port.id, retval));
|
|
}
|
|
|
|
LOG(0, (KERN_NOTICE "userif-%d: sent link %s event.\n",
|
|
userIf->port.id, linkUp ? "up" : "down"));
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VNetUserIf_Create --
|
|
*
|
|
* Create a user level port to the wonderful world of virtual
|
|
* networking.
|
|
*
|
|
* Results:
|
|
* Errno. Also returns an allocated port to connect to,
|
|
* NULL on error.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
VNetUserIf_Create(VNetPort **ret) // OUT
|
|
{
|
|
VNetUserIF *userIf;
|
|
static unsigned id = 0;
|
|
int retval;
|
|
|
|
userIf = kmalloc(sizeof *userIf, GFP_USER);
|
|
if (!userIf) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/*
|
|
* Initialize fields.
|
|
*/
|
|
|
|
userIf->port.id = id++;
|
|
|
|
userIf->port.jack.peer = NULL;
|
|
userIf->port.jack.numPorts = 1;
|
|
VNetSnprintf(userIf->port.jack.name, sizeof userIf->port.jack.name,
|
|
"userif%u", userIf->port.id);
|
|
userIf->port.jack.private = userIf;
|
|
userIf->port.jack.index = 0;
|
|
userIf->port.jack.procEntry = NULL;
|
|
userIf->port.jack.free = VNetUserIfFree;
|
|
userIf->port.jack.rcv = VNetUserIfReceive;
|
|
userIf->port.jack.cycleDetect = NULL;
|
|
userIf->port.jack.portsChanged = NULL;
|
|
userIf->port.jack.isBridged = NULL;
|
|
userIf->pollPtr = NULL;
|
|
userIf->actionIntr = NULL;
|
|
userIf->recvClusterCount = NULL;
|
|
userIf->pollPage = NULL;
|
|
userIf->actPage = NULL;
|
|
userIf->recvClusterPage = NULL;
|
|
userIf->pollMask = 0;
|
|
userIf->actionID = -1;
|
|
userIf->port.exactFilterLen = 0;
|
|
userIf->eventSender = NULL;
|
|
|
|
/*
|
|
* Make proc entry for this jack.
|
|
*/
|
|
|
|
retval = VNetProc_MakeEntry(userIf->port.jack.name, S_IFREG, userIf,
|
|
VNetUserIfProcRead,
|
|
&userIf->port.jack.procEntry);
|
|
if (retval) {
|
|
if (retval == -ENXIO) {
|
|
userIf->port.jack.procEntry = NULL;
|
|
} else {
|
|
kfree(userIf);
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Rest of fields.
|
|
*/
|
|
|
|
userIf->port.flags = IFF_RUNNING;
|
|
|
|
memset(userIf->port.paddr, 0, sizeof userIf->port.paddr);
|
|
memset(userIf->port.ladrf, 0, sizeof userIf->port.ladrf);
|
|
memset(userIf->port.exactFilter, 0, sizeof userIf->port.exactFilter);
|
|
|
|
VNet_MakeMACAddress(&userIf->port);
|
|
|
|
userIf->port.fileOpRead = VNetUserIfRead;
|
|
userIf->port.fileOpWrite = VNetUserIfWrite;
|
|
userIf->port.fileOpIoctl = VNetUserIfIoctl;
|
|
userIf->port.fileOpPoll = VNetUserIfPoll;
|
|
|
|
skb_queue_head_init(&(userIf->packetQueue));
|
|
init_waitqueue_head(&userIf->waitQueue);
|
|
|
|
memset(&userIf->stats, 0, sizeof userIf->stats);
|
|
|
|
*ret = &userIf->port;
|
|
return 0;
|
|
}
|
|
|