Beispiel #1
0
/*
 * Reads datagrams from the data in port and dispatches them. We
 * always start reading datagrams into only the first page of the
 * datagram buffer. If the datagrams don't fit into one page, we
 * use the maximum datagram buffer size for the remainder of the
 * invocation. This is a simple heuristic for not penalizing
 * small datagrams.
 *
 * This function assumes that it has exclusive access to the data
 * in port for the duration of the call.
 */
static void vmci_dispatch_dgs(unsigned long data)
{
	struct vmci_guest_device *vmci_dev = (struct vmci_guest_device *)data;
	u8 *dg_in_buffer = vmci_dev->data_buffer;
	struct vmci_datagram *dg;
	size_t dg_in_buffer_size = VMCI_MAX_DG_SIZE;
	size_t current_dg_in_buffer_size = PAGE_SIZE;
	size_t remaining_bytes;

	BUILD_BUG_ON(VMCI_MAX_DG_SIZE < PAGE_SIZE);

	ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR,
		    vmci_dev->data_buffer, current_dg_in_buffer_size);
	dg = (struct vmci_datagram *)dg_in_buffer;
	remaining_bytes = current_dg_in_buffer_size;

	while (dg->dst.resource != VMCI_INVALID_ID ||
	       remaining_bytes > PAGE_SIZE) {
		unsigned dg_in_size;

		/*
		 * When the input buffer spans multiple pages, a datagram can
		 * start on any page boundary in the buffer.
		 */
		if (dg->dst.resource == VMCI_INVALID_ID) {
			dg = (struct vmci_datagram *)roundup(
				(uintptr_t)dg + 1, PAGE_SIZE);
			remaining_bytes =
				(size_t)(dg_in_buffer +
					 current_dg_in_buffer_size -
					 (u8 *)dg);
			continue;
		}

		dg_in_size = VMCI_DG_SIZE_ALIGNED(dg);

		if (dg_in_size <= dg_in_buffer_size) {
			int result;

			/*
			 * If the remaining bytes in the datagram
			 * buffer doesn't contain the complete
			 * datagram, we first make sure we have enough
			 * room for it and then we read the reminder
			 * of the datagram and possibly any following
			 * datagrams.
			 */
			if (dg_in_size > remaining_bytes) {
				if (remaining_bytes !=
				    current_dg_in_buffer_size) {

					/*
					 * We move the partial
					 * datagram to the front and
					 * read the reminder of the
					 * datagram and possibly
					 * following calls into the
					 * following bytes.
					 */
					memmove(dg_in_buffer, dg_in_buffer +
						current_dg_in_buffer_size -
						remaining_bytes,
						remaining_bytes);
					dg = (struct vmci_datagram *)
					    dg_in_buffer;
				}

				if (current_dg_in_buffer_size !=
				    dg_in_buffer_size)
					current_dg_in_buffer_size =
					    dg_in_buffer_size;

				ioread8_rep(vmci_dev->iobase +
						VMCI_DATA_IN_ADDR,
					vmci_dev->data_buffer +
						remaining_bytes,
					current_dg_in_buffer_size -
						remaining_bytes);
			}

			/*
			 * We special case event datagrams from the
			 * hypervisor.
			 */
			if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
			    dg->dst.resource == VMCI_EVENT_HANDLER) {
				result = vmci_event_dispatch(dg);
			} else {
				result = vmci_datagram_invoke_guest_handler(dg);
			}
			if (result < VMCI_SUCCESS)
				dev_dbg(vmci_dev->dev,
					"Datagram with resource (ID=0x%x) failed (err=%d)\n",
					 dg->dst.resource, result);

			/* On to the next datagram. */
			dg = (struct vmci_datagram *)((u8 *)dg +
						      dg_in_size);
		} else {
			size_t bytes_to_skip;

			/*
			 * Datagram doesn't fit in datagram buffer of maximal
			 * size. We drop it.
			 */
			dev_dbg(vmci_dev->dev,
				"Failed to receive datagram (size=%u bytes)\n",
				 dg_in_size);

			bytes_to_skip = dg_in_size - remaining_bytes;
			if (current_dg_in_buffer_size != dg_in_buffer_size)
				current_dg_in_buffer_size = dg_in_buffer_size;

			for (;;) {
				ioread8_rep(vmci_dev->iobase +
						VMCI_DATA_IN_ADDR,
					vmci_dev->data_buffer,
					current_dg_in_buffer_size);
				if (bytes_to_skip <= current_dg_in_buffer_size)
					break;

				bytes_to_skip -= current_dg_in_buffer_size;
			}
			dg = (struct vmci_datagram *)(dg_in_buffer +
						      bytes_to_skip);
		}

		remaining_bytes =
		    (size_t) (dg_in_buffer + current_dg_in_buffer_size -
			      (u8 *)dg);

		if (remaining_bytes < VMCI_DG_HEADERSIZE) {
			/* Get the next batch of datagrams. */

			ioread8_rep(vmci_dev->iobase + VMCI_DATA_IN_ADDR,
				    vmci_dev->data_buffer,
				    current_dg_in_buffer_size);
			dg = (struct vmci_datagram *)dg_in_buffer;
			remaining_bytes = current_dg_in_buffer_size;
		}
	}
}
void
VMCI_ReadDatagramsFromPort(VMCIIoHandle ioHandle,  // IN
                           VMCIIoPort dgInPort,    // IN
                           uint8 *dgInBuffer,      // IN
                           size_t dgInBufferSize)  // IN
{
   VMCIDatagram *dg;
   size_t currentDgInBufferSize = PAGE_SIZE;
   size_t remainingBytes;

   ASSERT(dgInBufferSize >= PAGE_SIZE);

   VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer, currentDgInBufferSize);
   dg = (VMCIDatagram *)dgInBuffer;
   remainingBytes = currentDgInBufferSize;

   while (dg->dst.resource != VMCI_INVALID_ID || remainingBytes > PAGE_SIZE) {
      unsigned dgInSize;

      /*
       * When the input buffer spans multiple pages, a datagram can
       * start on any page boundary in the buffer.
       */

      if (dg->dst.resource == VMCI_INVALID_ID) {
         ASSERT(remainingBytes > PAGE_SIZE);
         dg = (VMCIDatagram *)ROUNDUP((uintptr_t)dg + 1, PAGE_SIZE);
         ASSERT((uint8 *)dg < dgInBuffer + currentDgInBufferSize);
         remainingBytes = (size_t)(dgInBuffer + currentDgInBufferSize - (uint8 *)dg);
         continue;
      }

      dgInSize = VMCI_DG_SIZE_ALIGNED(dg);

      if (dgInSize <= dgInBufferSize) {
         int result;

         /*
          * If the remaining bytes in the datagram buffer doesn't
          * contain the complete datagram, we first make sure we have
          * enough room for it and then we read the reminder of the
          * datagram and possibly any following datagrams.
          */

         if (dgInSize > remainingBytes) {

            if (remainingBytes != currentDgInBufferSize) {

               /*
                * We move the partial datagram to the front and read
                * the reminder of the datagram and possibly following
                * calls into the following bytes.
                */

               memmove(dgInBuffer, dgInBuffer + currentDgInBufferSize - remainingBytes,
                       remainingBytes);

               dg = (VMCIDatagram *)dgInBuffer;
            }

            if (currentDgInBufferSize != dgInBufferSize) {
               currentDgInBufferSize = dgInBufferSize;
            }

            VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer + remainingBytes,
                               currentDgInBufferSize - remainingBytes);
         }

         /* We special case event datagrams from the hypervisor. */
         if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
             dg->dst.resource == VMCI_EVENT_HANDLER) {
            result = VMCIEvent_Dispatch(dg);
         } else {
            result = VMCIDatagram_InvokeGuestHandler(dg);
         }
         if (result < VMCI_SUCCESS) {
            VMCI_DEBUG_LOG(4, (LGPFX"Datagram with resource (ID=0x%x) failed "
                               "(err=%d).\n", dg->dst.resource, result));
         }

         /* On to the next datagram. */
         dg = (VMCIDatagram *)((uint8 *)dg + dgInSize);
      } else {
         size_t bytesToSkip;

         /*
          * Datagram doesn't fit in datagram buffer of maximal size. We drop it.
          */

         VMCI_DEBUG_LOG(4, (LGPFX"Failed to receive datagram (size=%u bytes).\n",
                            dgInSize));

         bytesToSkip = dgInSize - remainingBytes;
         if (currentDgInBufferSize != dgInBufferSize) {
            currentDgInBufferSize = dgInBufferSize;
         }
         for (;;) {
            VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer, currentDgInBufferSize);
            if (bytesToSkip <= currentDgInBufferSize) {
               break;
            }
            bytesToSkip -= currentDgInBufferSize;
         }
         dg = (VMCIDatagram *)(dgInBuffer + bytesToSkip);
      }

      remainingBytes = (size_t) (dgInBuffer + currentDgInBufferSize - (uint8 *)dg);

      if (remainingBytes < VMCI_DG_HEADERSIZE) {
         /* Get the next batch of datagrams. */

         VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer, currentDgInBufferSize);
         dg = (VMCIDatagram *)dgInBuffer;
         remainingBytes = currentDgInBufferSize;
      }
   }
}