Exemple #1
0
int xferlen(THREAD *thp, IOV *iov, int parts) {
	int				len;
	int				status;
	jmp_buf			env;
	
	if(parts < 0) {
		return(-parts);
	}

#ifndef NDEBUG
	/* Make sure iov's address space is accessable */
	if(thp->aspace_prp && thp->aspace_prp != aspaces_prp[KERNCPU]) {
		crash();
	}
#endif

	xfer_env = &env;
	if((status = xfer_setjmp(*xfer_env))) {
		return(status);
	}

	SET_XFER_HANDLER(&xfer_fault_handlers_xferlen);

	len = 0;
	while(parts > 0) {
		len += GETIOVLEN(iov);
		++iov;
		--parts;
	}

	SET_XFER_HANDLER(0);

	return(len);
}
Exemple #2
0
int (xferiov)(THREAD *sthp, IOV *dst, IOV *src, int dparts, int sparts, int doff, int soff) {
	char		*daddr,  *saddr;
	unsigned	dlen, slen, ret;
	
#ifndef NDEBUG
if(doff > GETIOVLEN(dst)) crash();
#endif
	daddr = (char *)GETIOVBASE(dst) + doff;
	dlen = GETIOVLEN(dst) - doff;

#ifndef NDEBUG
if(soff > GETIOVLEN(src)) crash();
#endif
	saddr = (char *)GETIOVBASE(src) + soff;
	slen = GETIOVLEN(src) - soff;

	/* Now we move the data. */
	for(;;) {
		if(slen < dlen) {
			ret = xfer_cpy(daddr, saddr, slen);
			sthp->args.ms.msglen += slen;
			if((--sparts == 0) || ret) {
				break;
			}
			daddr += slen;
			dlen -= slen;
			++src;
			saddr = (char *)GETIOVBASE(src);
			slen  = GETIOVLEN(src);
		} else if(dlen < slen) {
			ret = xfer_cpy(daddr, saddr, dlen);
			sthp->args.ms.msglen += dlen;
			if((--dparts == 0) || ret) {
				break;
			}
			saddr += dlen;
			slen -= dlen;
			++dst;
			daddr = (char *)GETIOVBASE(dst);
			dlen  = GETIOVLEN(dst);
		} else {
			ret = xfer_cpy(daddr, saddr, slen);
			sthp->args.ms.msglen += slen;
			if((--dparts == 0) || (--sparts == 0) || ret) {
				break;
			}
			++src;
			saddr = (char *)GETIOVBASE(src);
			slen  = GETIOVLEN(src);
			++dst;
			daddr = (char *)GETIOVBASE(dst);
			dlen  = GETIOVLEN(dst);
		}
	}
	return(ret);
}
Exemple #3
0
/*---------------------------------------------------------------------------*/
size_t memclonev(struct xio_iovec *dst, int *dparts,
		 const struct xio_iovec *src, int sparts)
{
	size_t		    nbytes = 0;
	struct xio_iovec *pdst = dst;

	if (dparts)
		*dparts =  sparts;
	while (sparts > 0) {
		GETIOVBASE(pdst) = GETIOVBASE(src);
		GETIOVLEN(pdst) = GETIOVLEN(src);
		nbytes += GETIOVLEN(pdst);
		sparts--;
		pdst++;
		src++;
	}

	return nbytes;
}
Exemple #4
0
int
xferpulse(THREAD *dthp, IOV *dst, int parts, uint32_t code, uint32_t value, int32_t scoid)
{
	struct _pulse	*addr;
	uint32_t		len;
	uintptr_t		last;
	int				status;
	jmp_buf			env;

#ifndef NDEBUG
	/* Make sure iov's address space is accessable */
	if(dthp->aspace_prp && dthp->aspace_prp != aspaces_prp[KERNCPU]) {
		crash();
	}
#endif

	xfer_env = &env;
	if((status = xfer_setjmp(*xfer_env))) {
		return(status);
	}

	SET_XFER_HANDLER(&xfer_fault_handlers);

	/* Special case for 1 part messages */
	if(parts < 0) {
		addr = (struct _pulse *)dst;
		len = -parts;
	} else {
		addr = (struct _pulse *)GETIOVBASE(dst);
		len = GETIOVLEN(dst);
	}

	/* Make dest will hold a pulse */
	if(len < sizeof(*addr)) {
		SET_XFER_HANDLER(0);
		return(XFER_DST_FAULT);
	}

	/* Make sure address is within range for process */
	last = (uintptr_t)addr + len - 1;
	if((uintptr_t)addr > last || !WITHIN_BOUNDRY((uintptr_t)addr, last, dthp->process->boundry_addr)) {
		SET_XFER_HANDLER(0);
		return(XFER_DST_FAULT);
	}

	if (dthp->process->boundry_addr != VM_KERN_SPACE_BOUNDRY) {
		/*
		 * Trigger fault if page is not writable
		 */
		__asm__ __volatile__(
			"strbt	%0, [%1]"
			:
			: "r" (0), "r" (addr)
		);
	}
Exemple #5
0
inline size_t xio_iovex_length(const struct xio_iovec_ex *iov,
			       unsigned long nr_segs)
{
	size_t				nbytes = 0;
	const struct xio_iovec_ex	*piov = iov;

	while (nr_segs > 0) {
		nbytes += GETIOVLEN(piov);
		nr_segs--;
		piov++;
	}

	return nbytes;
}
Exemple #6
0
ssize_t _writexv(int fd, iov_t *iov, int nparts, unsigned xtype, void *xdata, size_t xdatalen, size_t nbytes) {
	io_write_t				msg;

	if(nparts < 1 || (int)xdatalen < 0) {
		errno = EINVAL;
		return -1;
	}
	msg.i.type = _IO_WRITE;
	msg.i.combine_len = sizeof msg.i;
	msg.i.xtype = xtype;
	msg.i.zero = 0;
	SETIOV(iov + 0, &msg.i, sizeof msg.i);
	if((msg.i.nbytes = nbytes) == 0) {
		int						i;

		for(i = 1; i < nparts; i++) {
			msg.i.nbytes += GETIOVLEN(&iov[i]);
		}
	}
	/* If the parts is negative, then the iov points to -nparts bytes of data */
	return MsgSendv(fd, iov, nparts, xdata, -xdatalen);
}
Exemple #7
0
int
vmm_map_xfer(PROCESS *actptp, PROCESS *prp, IOV **piov, int *pparts, int *poff, IOV *niov, int *pnparts, unsigned flags)
{
	IOV			iovlist[32];
	IOV			*iov;
	unsigned	n;
	unsigned	parts;
	unsigned	off;
	unsigned	nparts;
	unsigned	nparts_max;
	unsigned	nbytes;
	uintptr_t	last;
	uintptr_t	base;
	uintptr_t	len;
	IOV			*oiov    = *piov;
	unsigned	oparts   = *pparts;
	uintptr_t	iov_diff = 0;

	/*
	 * Calculate the remapped process virtual address
	 */
	CRASHCHECK( prp == NULL );	
	CRASHCHECK( prp->memory == NULL );
	
	xfer_prp  = prp;
	xfer_diff = MVA_BASE(prp->memory->cpu.asid);

	/*
	 * Check supplied iov array is valid
	 */
	if (!(flags & MAPADDR_FLAGS_IOVKERNEL)) {
		if ((uintptr_t)oiov < VM_USER_SPACE_BOUNDRY) {
			iov_diff = xfer_diff;
		}

		last = (uintptr_t)oiov + oparts * sizeof(IOV) - 1;
		if ((uintptr_t)oiov > last ||
			(!(flags & MAPADDR_FLAGS_SYSPRP) && !WITHIN_BOUNDRY((uintptr_t)oiov, last, prp->boundry_addr))) {
			return -1;
		}

		base = ((uintptr_t)oiov) & ~PGMASK;
		last = ((uintptr_t)(oiov + (uintptr_t)oparts)) & ~PGMASK;
		while (base <= last) {
			if (xfer_memprobe((void*)(base + iov_diff)) != 0) {
				return -1;
			}
			base += __PAGESIZE;
		}
	}

	/*
	 * Skip over supplied offset
	 */
	off = *poff;
	while (off >= (len = GETIOVLEN((IOV*)((uintptr_t)oiov + iov_diff)))) {
		off -= len;
		if(--oparts == 0) { 	/* No more parts. */
			*pnparts = 0;
			return 0;
		}
		++oiov;
	}

	iov  = (IOV *)((uintptr_t)oiov + iov_diff);
	base = (uintptr_t)GETIOVBASE(iov);
	len  = (uintptr_t)GETIOVLEN(iov);
	if (off) {
		base += off;
		len  -= off;
		off   = 0;
	}

	/*
	 * Don't adjust non-pidified addresses
	 */
	if (base >= USER_SIZE) {
		xfer_diff = 0;
		xfer_prp  = 0;
	}

	/*
	 * Adjust each iov base by xfer_diff
	 */
	n = min(sizeof iovlist / sizeof *iovlist, oparts);
	parts  = 0;
	nparts = 0;
	nbytes = 0;
	nparts_max = *pnparts;
	while (nparts < nparts_max) {
 		/*
 		 * Make sure address is within range for process
 		 */
		if (len) {
			last = base + len - 1;
			if (base > last || (!(flags & MAPADDR_FLAGS_SYSPRP) && !WITHIN_BOUNDRY(base, last, prp->boundry_addr))) {
				return -1;
			}

			SETIOV(niov, (IOV *)((uintptr_t)base + xfer_diff), len);
			nbytes += len;
			nparts++;
			niov++;
		}
		if (++parts >= n)
			break;
		iov++;
		base = (uintptr_t)GETIOVBASE(iov);
		len  = (uintptr_t)GETIOVLEN(iov);
	}

	/*
	 * Update the caller's iov list
	 */
	*piov    = oiov + parts;
	*pparts  = oparts - parts;
	*pnparts = nparts;
	*poff    = off;

	return nbytes;
}
Exemple #8
0
int
vmm_map_xfer(PROCESS *actprp, PROCESS *prp, IOV **piov, int *pparts, int *poff, IOV *niov, int *pnparts, unsigned flags)
{
	struct xfer_slot	*slot = &xfer_slots[RUNCPU];
	IOV					*iov = *piov;
	int					parts = *pparts;
	unsigned			bound = prp->boundry_addr;
	unsigned			diff0 = 0;
	unsigned			diff1 = 0;
	unsigned			lo0 = 0;
	unsigned			lo1 = 0;
	unsigned			hi0 = 0;
	unsigned			hi1 = 0;
	int					l1_mapped = 0;
	unsigned			addr;
	unsigned			last;
	unsigned			off;
	unsigned			size;
	int					nbytes;
	int					nparts;
	int					nparts_max;

#ifndef	NDEBUG
	if (actprp->memory == 0) {
		crash();
	}
	if (prp->memory == 0) {
		crash();
	}
#endif

	slot->prp = prp;
	slot->diff0 = 0;
	slot->size0 = 0;
	slot->diff1 = 0;
	slot->size1 = 0;

	/*
	 * Map IOV addresses if necessary
	 */
	if ((flags & MAPADDR_FLAGS_IOVKERNEL) == 0) {
		last = (uintptr_t)iov + parts * sizeof(IOV) - 1;
		if ((uintptr_t)iov > last || (!(flags & MAPADDR_FLAGS_SYSPRP) && !WITHIN_BOUNDRY((uintptr_t)iov, last, bound))) {
			return -1;
		}
		if (V6_USER_SPACE(iov)) {
			/*
			 * Map the iov list
			 */
			if (l1_mapped == 0) {
				l1_map(prp->memory);
				l1_mapped = 1;
			}
			lo0 = (unsigned)iov & ~ARM_SCMASK;
			hi0 = map_slot0(lo0, last);
			slot->diff0 = diff0 = ARM_V6_XFER_BASE - lo0;
			slot->size0 = hi0 - lo0;

			iov = (IOV *)((unsigned)iov + diff0);
		}
	}

	/*
	 * Check whole IOV list is valid
	 */
	addr = ((uintptr_t)iov) & ~PGMASK;
	last = ((uintptr_t)(iov + (uint32_t)parts) - 1) & ~PGMASK;
	if (addr > last) {
		nbytes = -1;
		goto out;
	}
	while (addr <= last) {
		if (xfer_memprobe((void *)addr) != 0) {
			nbytes = -1;
			goto out;
		}
		addr += __PAGESIZE;
	}

	/*
	 * Skip to supplied offset
	 */
	off = *poff;
	while (off >= (size = GETIOVLEN(iov))) {
		off -= size;
		if (--parts == 0) {
			nbytes = 0;		// no more parts
			goto out;
		}
		iov++;
	}
	addr = (unsigned)GETIOVBASE(iov) + off;
	size = (unsigned)GETIOVLEN(iov)  - off;
	off = 0;

	/*
	 * Loop over remaining IOVs and adjust to mapped addresses
	 */
	nbytes = 0;
	nparts = 0;
	nparts_max = *pnparts;
	while (nparts < nparts_max) {
		unsigned	map_size;
		unsigned	map_addr;
		unsigned	len;

		if (size) {
			/*
			 * Check addresses are valid
			 */
			last = addr + size - 1;
			if (addr > last || (!(flags & MAPADDR_FLAGS_SYSPRP) && !WITHIN_BOUNDRY(addr, last, bound))) {
				nbytes = -1;
				goto out;
			}
			if (!V6_USER_SPACE(addr)) {
				/*
				 * Kernel address - no mapping required
				 */
				map_size = size;
				map_addr = addr;
			}
			else if (addr >= lo0 && addr < hi0) {
				/*
				 * Contained in first mapping
				 */
				map_addr = addr + diff0;
				map_size = hi0 - addr;
			}
			else if (addr >= lo1 && addr < hi1) {
				/*
				 * Contained in second mapping
				 */
				map_addr = addr + diff1;
				map_size = hi1 - addr;
			}
			else if (hi1 == 0) {
				/*
				 * Create second set of mappings
				 */
				if (l1_mapped == 0) {
					l1_map(prp->memory);
					l1_mapped = 1;
				}
				lo1 = addr & ~ARM_SCMASK;
				hi1 = map_slot1(lo1, slot->size0);
				slot->diff1 = diff1 = ARM_V6_XFER_BASE + slot->size0 - lo1;
				slot->size1 = ARM_V6_XFER_SIZE - slot->size0;

				map_addr = addr + diff1;
				map_size = hi1 - addr;
			}
			else {
				/*
				 * Can't map the iov data
				 */
				break;
			}

			len = min(size, map_size);
			SETIOV(niov, map_addr, len);
			niov++;
			nparts++;
			nbytes += len;
			if (size > map_size) {
				/*
				 * Could only map part of the iov
				 */
				off = (addr - (unsigned)GETIOVBASE(iov)) + len;
				break;
			}
		}
		if (--parts == 0) {
			break;
		}
		iov++;
		addr = (unsigned)GETIOVBASE(iov);
		size = GETIOVLEN(iov);
	}

	/*
	 * Adjust caller's iov list to the end of the area we just mapped
	 */
	*piov = (IOV *)((unsigned)iov - diff0);
	*pparts = parts;
	*poff = off;
	*pnparts = nparts;

out:
	if (l1_mapped) {
		l1_unmap();
	}
	return nbytes;
}
Exemple #9
0
/*
 * memcpyv
 *
 * Copy data from one iov to another.
 */
size_t memcpyv(const struct iovec *dst, int dparts, int doff, const struct iovec *src, int sparts, int soff) {
	unsigned char	*saddr, *daddr;
	int				slen, dlen;
	size_t			nbytes;

	/* Check for a dst offset and skip over it. */
	while(doff >= (dlen = GETIOVLEN(dst))) {
		doff -= dlen;
		if(--dparts == 0) { 	/* No more parts. */
			return 0;
		}
		dst++;
	}
	dlen -= doff;
	daddr = (unsigned char *)GETIOVBASE(dst) + doff;

	/* Check for a src offset and skip over it. */
	while(soff >= (slen = GETIOVLEN(src))) {
		soff -= slen;
		if(--sparts == 0) { 	/* No more parts. */
			return 0;
		}
		src++;
	}
	slen -= soff;
	saddr = (unsigned char *)GETIOVBASE(src) + soff;

	/* Now we move the data. */
	nbytes = 0;
	for(;;) {
		int						len;

		/* Check how many bytes can be moved. */
		if((len = min(slen, dlen))) {
			nbytes += len;
			memcpy(daddr, saddr, len);
		}

		/* Adjust source. */
		saddr += len;
		if((slen -= len) == 0) {
			if(--sparts == 0) {
				break;
			}
			src++;
			saddr = (unsigned char *)GETIOVBASE(src);
			slen  = GETIOVLEN(src);
		}

		/* Adjust dest. */
		daddr += len;
		if((dlen -= len) == 0) {
			if(--dparts == 0) {
				break;
			}
			dst++;
			daddr = (unsigned char *)GETIOVBASE(dst);
			dlen  = GETIOVLEN(dst);
		}
	}

	return nbytes;
}
Exemple #10
0
int
vmm_map_xfer(PROCESS *actprp, PROCESS *prp,  IOV **piov, int *pparts, int *poff, IOV *niov, int *pnparts, unsigned flags) {
	unsigned	base0, base1;
	IOV 		*iov_phy, *iov;
	void 		*addr;
	unsigned	parts, size, addr_phy, nparts, nparts_max, bytes, bound;
	pte_t		**pgdir;
	uintptr_t	last, off;

#ifndef NDEBUG
	ADDRESS							*adp;

	if(!prp || !(adp = prp->memory) || !(pgdir = adp->cpu.pgdir)) {
		return -1;
	}
#else
	pgdir = prp->memory->cpu.pgdir;
#endif
	xfer_pgdir = pgdir;
	xfer_diff[0] = xfer_diff[1] = 0;
	base0 = base1 = 0;
	xfer_prp = prp;

	// check and map IOV address
	iov = (IOV*)*piov;
	parts = *pparts;
	bound = prp->boundry_addr;
	if(!(flags & MAPADDR_FLAGS_IOVKERNEL)) {
#ifndef NDEBUG
			// boundry check
			last = (uintptr_t)iov + parts*sizeof(IOV)  - 1;
			if((uintptr_t)iov > last || (!(flags & MAPADDR_FLAGS_SYSPRP) && !WITHIN_BOUNDRY((uintptr_t)iov, last, prp->boundry_addr))) {
				return -1;
			}
#endif
		if((uintptr_t)iov < VM_USER_SPACE_BOUNDRY) {
			// system process and kernel address space
			iov_phy = iov;
			flags |= MAPADDR_FLAGS_IOVKERNEL;
		} else {
			// User address
			base0 = (unsigned)iov & SR_MAP_MASK;
			iov_phy = (IOV *)(((uintptr_t)iov & ~SR_MAP_MASK) + MSG_SR_BASE0);
			xfer_diff[0] = MSG_SR_BASE0 - base0;
			// Set Segment register
			MSG_XFER_SET_SR(1, (ADDRESS *)prp->memory, base0);
		}
	} else {
		iov_phy = iov;
	}

	// skip offsets
	off = *poff;
	while( off >= (size = GETIOVLEN(iov_phy)) ) {
		off -= size;
		if(--parts == 0) { 	/* No more parts. */
			*pnparts = parts;
			return 0;
		}
		++iov_phy;
	}

	// first iov
	size -= off;
	addr = (char *)GETIOVBASE(iov_phy) + off;

	// mapping loop
	nparts_max = *pnparts;
	bytes = 0;
	nparts = 0;

	do {
		int len;
		// boundry check
		last = (uintptr_t)addr + size - 1;
		if((uintptr_t)addr > last || (!(flags & MAPADDR_FLAGS_SYSPRP) && !WITHIN_BOUNDRY((uintptr_t)addr, last, bound))) {
			return -1;
		}

		if(((uintptr_t)addr & SR_MAP_MASK) == (((uintptr_t)addr+size-1) & SR_MAP_MASK)) {
			len = size;
		} else {	
			len = (((uintptr_t)addr & SR_MAP_MASK) + 0x10000000) - (uintptr_t)addr;
		}
				
		if((uintptr_t)addr < VM_USER_SPACE_BOUNDRY) {
			// system process and kernel address space
			SETIOV(niov, addr, size);
			bytes += size;
			size = 0;
			niov ++;
			nparts ++;
		} else if (((uintptr_t)addr & SR_MAP_MASK) == base0) {
			
			addr_phy = (unsigned)(((uintptr_t)addr & ~SR_MAP_MASK) + MSG_SR_BASE0);
			SETIOV(niov, addr_phy, len);
			bytes += len;
			size -= len;
			niov ++;
			nparts ++;
		} else if (((uintptr_t)addr & SR_MAP_MASK) == base1) {
			
			addr_phy = (unsigned)(((uintptr_t)addr & ~SR_MAP_MASK) + MSG_SR_BASE1);
			SETIOV(niov, addr_phy, len);
			bytes += len;
			size -= len;
			niov ++;
			nparts ++;
		} else {
			if(!base0) {
				base0 = (unsigned)addr & SR_MAP_MASK;
				xfer_diff[0] = MSG_SR_BASE0 - base0;
				// Set Segment register
				MSG_XFER_SET_SR(1, (ADDRESS *)prp->memory, base0);
				addr_phy = (unsigned)(((uintptr_t)addr & ~SR_MAP_MASK) + MSG_SR_BASE0);
				SETIOV(niov, addr_phy, len);
				bytes += len;
				size -= len;
				niov ++;
				nparts ++;
			} else if(!base1) {
				base1 = (unsigned)addr & SR_MAP_MASK;
				xfer_diff[1] = MSG_SR_BASE1 - base1;
				// Set Segment register
				MSG_XFER_SET_SR(2, (ADDRESS *)prp->memory, base1);
				addr_phy = (unsigned)(((uintptr_t)addr & ~SR_MAP_MASK) + MSG_SR_BASE1);
				SETIOV(niov, addr_phy, len);
				bytes += len;
				size -= len;
				niov ++;
				nparts ++;
			} else {
				break;
			}
		} //end of if kernel space

		if(nparts >= nparts_max) {
			if(size == 0) parts --;
			break;
		}

		if(size != 0) {
			// Exception case, we crossed over a 256MB boundary
			addr = (void *)((uintptr_t)addr + len);
			// Size has already been adjusted
			continue;
		}
		// need to check the address of next source iov
		do {
			if(--parts == 0) break;
			iov_phy ++;
			addr = GETIOVBASE(iov_phy);
			size = GETIOVLEN(iov_phy);
		} while (size == 0);
	} while(parts);

	if(parts != 0) {
		*piov = *piov + (*pparts - parts);
		if(size == 0) {
			*poff = 0;
		} else {
			*poff = GETIOVLEN(iov_phy) - size;
		} 
	} 
	*pparts = parts;
	*pnparts = nparts;

	return bytes;
}
Exemple #11
0
int kdecl
ker_msg_receivev(THREAD *act, struct kerargs_msg_receivev *kap) {
	CHANNEL		*chp;
	CONNECT		*cop;
	THREAD		*thp;
	THREAD		**owner;
	int			 tid, chid;
	unsigned	 tls_flags;
	VECTOR		*chvec;

	chid = act->last_chid = kap->chid;		// Used for priority boost
	chvec = &act->process->chancons;

	if(chid & _NTO_GLOBAL_CHANNEL) {
		chid &= ~_NTO_GLOBAL_CHANNEL;
		chvec = &chgbl_vector;
	}
	if((chp = vector_lookup(chvec, chid)) == NULL  ||
	   chp->type != TYPE_CHANNEL) {
	   	lock_kernel();
		return ESRCH;
	}

	if(kap->info) {
		WR_VERIFY_PTR(act, kap->info, sizeof(*kap->info));
		// NOTE:
		// Make sure the receive info pointer is valid. Note that we need some
		// extra checks in the mainline when filling in the rcvinfo (this is no
		// longer done in specret).
		//
		// Note: we don't probe the whole buffer, rather touch start and end,
		// which is faster and sufficient
		//
		WR_PROBE_INT(act, kap->info, 1);
		WR_PROBE_INT(act, &kap->info->reserved, 1);
	}

	if(chp->flags & (_NTO_CHF_ASYNC | _NTO_CHF_GLOBAL)) {
		if(chp->flags & _NTO_CHF_GLOBAL) {
			cop = NULL;
			if(kap->coid) {
				if((cop = lookup_connect(kap->coid)) == NULL  ||  cop->type != TYPE_CONNECTION) {
					return EBADF;
				}
			}

			return msgreceive_gbl(act, (CHANNELGBL*) chp, kap->rmsg, -kap->rparts, kap->info, cop, kap->coid);
		} else {
			return msgreceive_async(act, (CHANNELASYNC*) chp, kap->rmsg, kap->rparts);
		}
	}

	/*
	 * Validate incoming IOVs and calculate receive length
	 */
 	if(kap->rparts >= 0) {
		int len = 0;
		int len_last = 0;
		IOV *iov = kap->rmsg;
		int rparts = kap->rparts;

		if (kap->rparts != 0) {
			if (!WITHIN_BOUNDRY((uintptr_t)iov, (uintptr_t)(&iov[rparts]), act->process->boundry_addr)) {
				return EFAULT;
			}
		}

		// Calculate receive length -- even if not requested, we use it for msginfo
		// Do boundary check
		while(rparts) {
			uintptr_t base, last;

			len += GETIOVLEN(iov);
			if (len <len_last ) {
				/*overflow. excessively long user IOV, possibly overlayed. pr62575 */
				return EOVERFLOW;
			}
			len_last = len;
			base = (uintptr_t)GETIOVBASE(iov);
			last = base + GETIOVLEN(iov) - 1;
			if(((base > last) || !WITHIN_BOUNDRY(base, last, act->process->boundry_addr)) && (GETIOVLEN(iov) != 0)) {
				return EFAULT;
			}
			++iov;
			--rparts;
		}
		act->args.ms.srcmsglen = len;
	} else {
		// Single part -- validate receive address
		uintptr_t base, last;
		base = (uintptr_t) kap->rmsg;
		last = base + (-kap->rparts) - 1;
		if((base > last) || !WITHIN_BOUNDRY(base, last, act->process->boundry_addr)) {
			// We know length is non-zero from test above
			return EFAULT;
		}
		act->args.ms.srcmsglen = -kap->rparts;
	}


restart:
	// Was there was a waiting thread or pulse on the channel?
	thp = pril_first(&chp->send_queue);
restart2:
	if(thp) {
		int xferstat;
		unsigned	type = TYPE_MASK(thp->type);

		// Yes. There is a waiting message.
		if((type == TYPE_PULSE) || (type == TYPE_VPULSE)) {
			PULSE *pup = (PULSE *)(void *)thp;

			act->restart = NULL;
			xferstat = xferpulse(act, kap->rmsg, kap->rparts, pup->code, pup->value, pup->id);

			if(type == TYPE_VPULSE) {
				thp = (THREAD *)pup->id;
				get_rcvinfo(thp, -1, thp->blocked_on, kap->info);
			}

			lock_kernel();
			act->timeout_flags = 0;

			// By default the receiver runs with message driven priority.
			// RUSH: Fix for partition inheritance
			if(act->priority != pup->priority  &&  (chp->flags & _NTO_CHF_FIXED_PRIORITY) == 0) {
				adjust_priority(act, pup->priority, act->process->default_dpp, 1);
				act->real_priority = act->priority;
			} else if(act->dpp != act->process->default_dpp) {
				adjust_priority(act, act->priority, act->process->default_dpp, 1);
			}

			pulse_remove(chp->process, &chp->send_queue, pup);

			if((thp = act->client) != 0) {
				/* need to clear client's server field */
				act->client = 0;
				thp->args.ms.server = 0;
			}

			if(xferstat) {
				return EFAULT;
			}
			_TRACE_COMM_IPC_RET(act);

			return EOK;
		}

		// If the receive request was for a pulse only, keep checking the list..
		if(KTYPE(act) == __KER_MSG_RECEIVEPULSEV) {
			thp = thp->next.thread;
			goto restart2;
		}

#if defined(VARIANT_smp) && defined(SMP_MSGOPT)
		// If thp is in the xfer status in another CPU, try next one
		if(thp->internal_flags & _NTO_ITF_MSG_DELIVERY) {
			thp = thp->next.thread;
			goto restart2;
		}
#endif

		// If an immediate timeout was specified we unblock the sender.
		if(IMTO(thp, STATE_REPLY)) {
			lock_kernel();
			force_ready(thp, ETIMEDOUT);
			unlock_kernel();
			KER_PREEMPT(act, ENOERROR);
			goto restart;
		}

		if(thp->flags & _NTO_TF_BUFF_MSG) {
			xferstat = xfer_cpy_diov(act, kap->rmsg, thp->args.msbuff.buff, kap->rparts, thp->args.msbuff.msglen);
		} else {
			act->args.ri.rmsg = kap->rmsg;
			act->args.ri.rparts = kap->rparts;

			START_SMP_XFER(act, thp);

			xferstat = xfermsg(act, thp, 0, 0);

			lock_kernel();
			END_SMP_XFER(act, thp);

#if defined(VARIANT_smp) && defined(SMP_MSGOPT)
			if(thp->internal_flags & _NTO_ITF_MSG_FORCE_RDY) {
				force_ready(thp,KSTATUS(thp));
				thp->internal_flags &= ~_NTO_ITF_MSG_FORCE_RDY;
				KERCALL_RESTART(act);
				act->restart = 0;
				return ENOERROR;
			}
			if(act->flags & (_NTO_TF_SIG_ACTIVE | _NTO_TF_CANCELSELF)) {
				KERCALL_RESTART(act);
				act->restart = 0;
				return ENOERROR;
			}
#endif
		}

		if(xferstat) {
			lock_kernel();

			// Only a send fault will unblock the sender.
			if(xferstat & XFER_SRC_FAULT) {
				// Let sender know it faulted and restart receive.
				force_ready(thp, EFAULT);
				unlock_kernel();
				KER_PREEMPT(act, ENOERROR);
				goto restart;
			}

			if((thp = act->client) != 0) {
				/* need to clear client's server field */
				act->client = 0;
				thp->args.ms.server = 0;
			}

			// Let receiver and sender know reason for fault.
			act->timeout_flags = 0;
			return EFAULT;
		}

		if(TYPE_MASK(thp->type) == TYPE_VTHREAD) {
			tid = thp->args.ri.rparts;
		} else {
			tid = thp->tid;
		}
		cop = thp->blocked_on;
		if(thp->args.ms.srcmsglen == ~0U) {
			// This should never occur with the new code
			crash();
			/* NOTREACHED */
			thp->args.ms.srcmsglen = thp->args.ms.msglen;
		}

		// If the receive specified an info buffer stuff it as well.
		// thp->args.ms.msglen was set by xfermsg
		if(kap->info) {
		//	get_rcvinfo(thp, -1, cop, kap->info);
			STUFF_RCVINFO(thp, cop, kap->info);
			if(thp->flags & _NTO_TF_BUFF_MSG) {
				if(kap->info->msglen > act->args.ms.srcmsglen) kap->info->msglen = act->args.ms.srcmsglen;
			}
		}

		lock_kernel();
		_TRACE_COMM_IPC_RET(act);
		act->timeout_flags = 0;
		act->restart = NULL;

		// Because _NTO_TF_RCVINFO and _NTO_TF_SHORT_MSG will not be set, set this to NULL
		thp->restart = NULL;

		if(act->client != 0) {
			/* need to clear client's server field */
			act->client->args.ms.server = 0;
		}
		thp->args.ms.server = act;
		act->client = thp;

		pril_rem(&chp->send_queue, thp);
		if(thp->state == STATE_SEND) {
			thp->state = STATE_REPLY;
			snap_time(&thp->timestamp_last_block,0);
			_TRACE_TH_EMIT_STATE(thp, REPLY);
			SETKSTATUS(act, (tid << 16) | cop->scoid);
		} else {
			thp->state = STATE_NET_REPLY;
			_TRACE_TH_EMIT_STATE(thp, NET_REPLY);
			SETKSTATUS(act, -((tid << 16) | cop->scoid));
		}
		LINKPRIL_BEG(chp->reply_queue, thp, THREAD);

		// By default the receiver runs with message driven priority.
		// RUSH: Fix for partition inheritance
		if((act->priority != thp->priority || act->dpp != thp->dpp) &&  (chp->flags & _NTO_CHF_FIXED_PRIORITY) == 0) {
			AP_INHERIT_CRIT(act, thp);
			adjust_priority(act, thp->priority, thp->dpp, 1);
			if(act->real_priority != act->priority) act->real_priority = act->priority;
		} else {
			AP_CLEAR_CRIT(act);
		}

		return ENOERROR;
	}

	// No-one waiting for a msg so block
	tls_flags = act->un.lcl.tls->__flags;
	lock_kernel();
	_TRACE_COMM_IPC_RET(act);

	if((thp = act->client) != 0) {
		/* need to clear client's server field */
		act->client = 0;
		thp->args.ms.server = 0;
	}

	if(IMTO(act, STATE_RECEIVE)) {
		return ETIMEDOUT;
	}

	if(PENDCAN(tls_flags)) {
		SETKIP_FUNC(act, act->process->canstub);
		return ENOERROR;
	}

	// Can't call block() here, because act may not be actives[KERNCPU]
	// anymore - if the sender faulted, we call force_ready() above and
	// that might change actives[KERNCPU]
	unready(act, STATE_RECEIVE);

	// End inheritance of partition and critical state. This must be after block() so that we microbill
	// the partition we where running in before we reset to the original partition. PR26990
	act->dpp = act->orig_dpp;
	AP_CLEAR_CRIT(act);

	act->blocked_on = chp;
	act->args.ri.rmsg = kap->rmsg;
	act->args.ri.rparts = kap->rparts;
	act->args.ri.info = kap->info;

	// Add to the receive queue, put pulse only receives at the end of
	// the list so the ker_msg_send() only has to check the head of the list
	owner = &chp->receive_queue;
	if(KTYPE(act) == __KER_MSG_RECEIVEPULSEV) {
		act->internal_flags |= _NTO_ITF_RCVPULSE;
		for( ;; ) {
			thp = *owner;
			if(thp == NULL) break;
			if(thp->internal_flags & _NTO_ITF_RCVPULSE) break;
			owner = &thp->next.thread;
		}
	}
	LINKPRIL_BEG(*owner, act, THREAD);
	return ENOERROR;
}
Exemple #12
0
int kdecl
ker_msg_sendv(THREAD *act, struct kerargs_msg_sendv *kap) {
	CONNECT		*cop;
	CHANNEL		*chp;
	int			 type = KTYPE(act);
	THREAD		*thp;
	THREAD		*sender;
	PROCESS		*actprp = act->process;
	unsigned	th_flags = 0;
	uint32_t	net_srcmsglen = -1U;


	/*
	 * These are the usual incoming checks
	 *  - validate connection
	 *  - get channel pointer
	 *  - check for cancellation
	 */

	// Lookup src connect.
	if((cop = inline_lookup_connect(actprp, kap->coid)) == NULL || cop->type != TYPE_CONNECTION) {
		return EBADF;
	}

	// Get dst channel.
	if((chp = cop->channel) == NULL) {
		return EBADF;
	}

	 _TRACE_COMM_EMIT_SMSG(act, cop, (act->tid << 16) | cop->scoid);

	if(PENDCAN(act->un.lcl.tls->__flags) && (type != __KER_MSG_SENDVNC)) {
		lock_kernel();
		SETKIP_FUNC(act, act->process->canstub);
		return ENOERROR;
	}

	/*
	 * The base conditions are now met. If this is a netcon or async channel,
	 * we handle separately
	 */
	 if(chp->flags & (_NTO_CHF_ASYNC | _NTO_CHF_GLOBAL)) {
		if(chp->flags & _NTO_CHF_GLOBAL) {
			return msgsend_gbl(act, cop, kap->smsg, -kap->sparts, (unsigned)-kap->rparts, kap->coid);
		} else {
			return msgsend_async(act, cop);
		}
	 }

	 sender = act;

	// Store incoming args
	if(cop->flags & COF_NETCON) {
		RD_PROBE_INT(act, kap->rmsg, sizeof(struct _vtid_info) / sizeof(int));
		sender = (THREAD *)(void *)net_send1(kap->rparts, (struct _vtid_info *)(void *)kap->rmsg);
		if(sender == NULL) {
			return EINVAL;
		}
		if(sender->state != STATE_STOPPED) crash();
		sender->args.ms.rmsg = kap->rmsg;
		sender->args.ms.rparts = kap->rparts;
		act->args.ms.smsg = kap->smsg;
		act->args.ms.sparts = kap->sparts;
		// Do this up-front while we have addressabilty
		net_srcmsglen = ((struct _vtid_info *)(void *)kap->rmsg)->srcmsglen;
	} else {
		sender->args.ms.coid = kap->coid;
		sender->args.ms.rmsg = kap->rmsg;
		sender->args.ms.rparts = kap->rparts;
	}

	sender->flags &= ~_NTO_TF_BUFF_MSG;
	// Make sure the SPECRET_PENDING bit isn't set when we don't need it.
	sender->internal_flags &= ~_NTO_ITF_SPECRET_PENDING;

	// Validate incoming IOVs - override for QNET case - rparts/rmsg have special meaning
	if(cop->flags & COF_NETCON) {
		sender->args.ms.dstmsglen = ((struct _vtid_info *)(void *)kap->rmsg)->dstmsglen;
	} else if(kap->rparts >= 0) {
		int len = 0;
		int len_last = 0;
		IOV *iov = kap->rmsg;
		int rparts = kap->rparts;
		int niov = 0;

		// Incoming reply IOV -- make copy of reply IOVs
		// Calculate reply length -- even if not requested, it is almost free
		// Also do boundary check
		while(rparts) {
			uintptr_t base, last;

			len += GETIOVLEN(iov);
			if (len <len_last ) {
				/*overflow. excessively long user IOV, possibly overlayed. pr62575 */
				return EOVERFLOW;
			}
			len_last=len;
			base = (uintptr_t)GETIOVBASE(iov);
			last = base + GETIOVLEN(iov) - 1;
			if(((base > last) || !WITHIN_BOUNDRY(base, last, sender->process->boundry_addr)) && (GETIOVLEN(iov) != 0)) {
				return EFAULT;
			}
			// Keep copy of IOV
			if(niov < _NUM_CACHED_REPLY_IOV) {
			//	sender->args.ms.riov[niov] = *iov;
			}
			++iov;
			++niov;
			--rparts;
		}
		sender->args.ms.dstmsglen = len;
	} else {
		// Single part -- validate and store reply address
		uintptr_t base, last;
		base = (uintptr_t) kap->rmsg;
		last = base + (-kap->rparts) - 1;
		if((base > last) || !WITHIN_BOUNDRY(base, last, sender->process->boundry_addr)) {
			// We know length is non-zero from test above
			return EFAULT;
		}
		sender->args.ms.dstmsglen = -kap->rparts;
	}


	/* Send IOVs */
	if(kap->sparts < 0) {
		// Single part -- do the boundary check and copy if short message
		uintptr_t base, last;
		int	len;

		base = (uintptr_t) kap->smsg;
		len = -kap->sparts;
		last = base + len - 1;
		if((base > last) || !WITHIN_BOUNDRY(base, last, sender->process->boundry_addr)) {
			// We know length is non-zero from test above
			return EFAULT;
		}
		sender->args.ms.srcmsglen = len;

		if(len <= sizeof(sender->args.msbuff.buff)) {
			(void)__inline_xfer_memcpy(sender->args.msbuff.buff, (char *)base, sender->args.msbuff.msglen = len);
			th_flags = _NTO_TF_BUFF_MSG;
		}
	} else if(kap->sparts == 1) {
		// Single IOV -- do the boundary check and copy if short message
		uintptr_t base, last, len;

		base = (uintptr_t)GETIOVBASE(kap->smsg);
		len = GETIOVLEN(kap->smsg);
		last = base + len - 1;
		if(((base > last) || !WITHIN_BOUNDRY(base, last, sender->process->boundry_addr)) && (len != 0)) {
			return EFAULT;
		}
		sender->args.ms.srcmsglen = len;
		if(len <= sizeof(sender->args.msbuff.buff)) {
			(void)__inline_xfer_memcpy(sender->args.msbuff.buff, (char *)base, sender->args.ms.msglen = len);
			th_flags = _NTO_TF_BUFF_MSG;
		}
	} else {
		// Multi IOV case
		int len = 0;
		int len_last =0;
		IOV *iov = kap->smsg;
		int sparts = kap->sparts;

		// Calculate send length -- even if not requested, it is almost free
		// Also do boundary check
		while(sparts) {
			uintptr_t base, last;

			len += GETIOVLEN(iov);
			if (len <len_last ) {
				/*overflow. excessively long user IOV, possibly overlayed. pr62575 */
				return EOVERFLOW;
			}
			len_last = len;
			base = (uintptr_t)GETIOVBASE(iov);
			last = base + GETIOVLEN(iov) - 1;
			if(((base > last) || !WITHIN_BOUNDRY(base, last, sender->process->boundry_addr)) && (GETIOVLEN(iov) != 0)) {
				return EFAULT;
			}
			++iov;
			--sparts;
			// Keep copy of IOV -- NYI, only really need if no receiver
			//if(niov < _NUM_CACHED_SEND_IOV) {
			//	sender->args.ms.siov[niov] = *iov;
			//}
		}
		sender->args.ms.srcmsglen = len;
		if(len <= sizeof(sender->args.msbuff.buff)) {
			int pos = 0;
			iov = kap->smsg;
			sparts = kap->sparts;
			// Multi-IOV incoming message that is short
			// FIXME -- need memcpy_siov for efficiency
			while(sparts) {
				int ilen = GETIOVLEN(iov);
				__inline_xfer_memcpy(&sender->args.msbuff.buff[pos], GETIOVBASE(iov), ilen);

				pos += ilen;
				iov++;
				sparts--;
			}
			sender->args.ms.msglen = len;
			th_flags = _NTO_TF_BUFF_MSG;
		}
	}

	// Now that the up-front business is done, we do the actual copy. If
	// this was identified as a short message, we have copied the message into the msgbuff area.

	// Was there was a waiting thread on the channel?

	thp = chp->receive_queue;
#if defined(VARIANT_smp) && defined(SMP_MSGOPT)
	while((thp != NULL) && (thp->internal_flags & _NTO_ITF_MSG_DELIVERY)) {
		thp = thp->next.thread;
	}
#endif
	if((thp != NULL) && !(thp->internal_flags & _NTO_ITF_RCVPULSE) ) {

		int xferstat;
		// If an immediate timeout was specified we return immediately.
		if(IMTO(act, STATE_REPLY)) {
			sender->flags &= ~_NTO_TF_BUFF_MSG;
			return ETIMEDOUT;
		}

		// Is this a long message?
		if(th_flags == 0) {
			sender->args.ms.smsg = kap->smsg;
			sender->args.ms.sparts = kap->sparts;
			START_SMP_XFER(act, thp);
			// Yes. Transfer the data.
			xferstat = xfermsg(thp, act, 0, 0);
			sender->args.ms.msglen = act->args.ms.msglen;

			lock_kernel();
			END_SMP_XFER(act, thp);

#if defined(VARIANT_smp) && defined(SMP_MSGOPT)
			if(thp->internal_flags & _NTO_ITF_MSG_FORCE_RDY) {
				force_ready(thp,KSTATUS(thp));
				thp->internal_flags &= ~_NTO_ITF_MSG_FORCE_RDY;
				KERCALL_RESTART(act);
				act->restart = 0;
				return ENOERROR;
			}
			if(act->flags & (_NTO_TF_SIG_ACTIVE | _NTO_TF_CANCELSELF)) {
				/* send is a cancelation point */
				KERCALL_RESTART(act);
				act->restart = 0;
				return ENOERROR;
			}
#endif

			if(xferstat) {
				lock_kernel();
				// If sender faulted let him know and abort the operation
				// without waking up the receiver.
				if(xferstat & XFER_SRC_FAULT) {
					goto send_fault;
				}
				// If receiver faulted, wake him up with an error and fail the
				// send.
				goto rcv_fault;
			}
		} else {

			// Short message. We do the following:
			// - switch aspace to receiver
			if(thp->aspace_prp && thp->aspace_prp != aspaces_prp[KERNCPU]) {
				/*
				 * Lock/unlock kernel if necessary before calling memmgr.aspace
				 */
				SWITCH_ASPACE(thp->aspace_prp, &aspaces_prp[KERNCPU], act);
			}
			// - copy message and handle errors
			if((xferstat = xfer_cpy_diov(thp, thp->args.ri.rmsg, sender->args.msbuff.buff, thp->args.ri.rparts, sender->args.msbuff.msglen))) {
				lock_kernel();
				// Has to be a receiver fault;
				goto rcv_fault;
			}
			sender->flags |= _NTO_TF_BUFF_MSG;
			// Note: below this point, we should NOT reference kap anywhere
			// as kap points to the original aspace
		}


		// If the receive specified an info buffer stuff it as well.
		// However, we are not in the address space of the destination
		// thread, we switch now
		thp->restart = NULL;

		if(thp->args.ri.info)  {
			struct _msg_info *repp = thp->args.ri.info;
			// Fill in rcvinfo
			// Switch to aspace of receiver. It's already adjusted if short msg.
			if(th_flags == 0) {
				if(thp->aspace_prp && thp->aspace_prp != aspaces_prp[KERNCPU]) {
					/*
					 * Kernel is already locked so we don't need SWITCH_ASPACE
					 */
					memmgr.aspace(thp->aspace_prp,&aspaces_prp[KERNCPU]);
				}
				if(cop->flags & COF_NETCON) {
					// Note: have to adjust srcmsglen before stuffing rcvinfo!
					sender->args.ms.srcmsglen = net_srcmsglen;
				}
			}
			// We can use a fast inline version as we know the thread does not
			// have an unblock pending
			STUFF_RCVINFO(sender, cop, thp->args.ri.info);

			// RUSH: Adjust msglen in better fashion...
			if(thp->args.ms.srcmsglen < repp->msglen) {
				repp->msglen = thp->args.ms.srcmsglen;
			}
		}

		lock_kernel();
		SETKSTATUS(thp, (sender->tid << 16) | cop->scoid);

		// Unlink receive thread from the receive queue.
		LINKPRIL_REM(thp);

		sender->args.ms.server = thp;
		thp->client = sender;

		// Check fast path conditions - no timeouts, no QNET, no sporadic.
		// We can inline the block_and_ready()
		if((sender->timeout_flags == 0) &&
			(thp->timeout_flags == 0) &&
			!(cop->flags & COF_NETCON) &&
			!(chp->flags & _NTO_CHF_FIXED_PRIORITY) &&
			!IS_SCHED_SS(sender)) {

			// By default the receiver runs with message driven priority.
			thp->real_priority = thp->priority = sender->priority;
			thp->dpp = sender->dpp;
			AP_INHERIT_CRIT(thp, sender);

			sender->state = STATE_REPLY;	// Must be set before calling block_and_ready()
			snap_time(&sender->timestamp_last_block,0);
			_TRACE_TH_EMIT_STATE(sender, REPLY);
#if defined(INLINE_BLOCKANDREADY)
			// This is an inline version of block an ready
			// We can use this for non-SMP (no runmask).
			// This also works for AP as we inherit the partition
			thp->next.thread = NULL;
			thp->prev.thread = NULL;
#ifdef _mt_LTT_TRACES_	/* PDB */
			//mt_TRACE_DEBUG("PDB 4.2");
			//mt_trace_var_debug(actives[KERNCPU]->process->pid, actives[KERNCPU]->tid, actives[KERNCPU]);
			mt_trace_task_suspend(actives[KERNCPU]->process->pid, actives[KERNCPU]->tid);
#endif
			//thp->restart = NULL;
			actives[KERNCPU] = thp;
			thp->state = STATE_RUNNING;
			//@@@ Hmm. This inline version of block_and_ready() may cause a small inaccuacy with APS.
			//thp->runcpu = KERNCPU;
#ifdef _mt_LTT_TRACES_	/* PDB */
			//mt_TRACE_DEBUG("PDB 4.3");
			//mt_trace_var_debug(thp->process->pid, thp->tid, thp);
			mt_trace_task_resume(thp->process->pid, thp->tid);
#endif
			_TRACE_TH_EMIT_STATE(thp, RUNNING);
#else
			block_and_ready(thp);
#endif
		} else {
			if((chp->flags & _NTO_CHF_FIXED_PRIORITY) == 0) {
				// By default the receiver runs with message driven priority.
				thp->real_priority = thp->priority = sender->priority;
				thp->dpp = sender->dpp;
				AP_INHERIT_CRIT(thp, sender);
			}
			sender->state = STATE_REPLY;	// Must be set before calling block_and_ready()
			_TRACE_TH_EMIT_STATE(sender, REPLY);

			if(cop->flags & COF_NETCON) {
				SETKSTATUS(act, 1);
				if((sender->flags & _NTO_TF_BUFF_MSG) == 0) {
					// #### Note: use net_srcmsglen saved above before we switch aspace
					sender->args.ms.srcmsglen = net_srcmsglen;
				}

				SETKSTATUS(thp, (sender->args.ms.rparts << 16) | cop->scoid);
				ready(thp);
			} else {
				block_and_ready(thp);
			}

			if(thp->timeout_flags & _NTO_TIMEOUT_REPLY) {
				// arm the timeout for reply block
				timeout_start(thp);
			}
		}

		// Block the active thread and ready the receiver thread
		sender->blocked_on = cop;

		// Link the now reply blocked sending thread in the reply queue
		LINKPRIL_BEG(chp->reply_queue, sender, THREAD);
		++cop->links;

		return ENOERROR;
	}

	// No-one waiting for a msg
	// If a normal thread
	//     Block the active thread
	//     Link the now send blocked thread into the reply queue
	// If a network thread send
	//     Link the passed vthread into the reply queue
	// Boost the servers priority to the clients if needed.
	if(th_flags == 0) {
		sender->args.ms.smsg = kap->smsg;
		sender->args.ms.sparts = kap->sparts;
			// FUTURE: Make copy of send IOVs
	} else {
		sender->flags |= _NTO_TF_BUFF_MSG;
	}


	if(IMTO(sender, STATE_SEND)) {
		sender->flags &= ~_NTO_TF_BUFF_MSG;
		return ETIMEDOUT;
	}

	lock_kernel();

	// Incoming network Send.
	// We use vtid passed in kap->rparts and _vtid_info passed in kap->rmsg
	if(cop->flags & COF_NETCON) {
		if(sender->flags & _NTO_TF_BUFF_MSG) {
			SETKSTATUS(act, 1);
		} else {
			// Return zero telling the network manager we still need the send data.
			// A _PULSE_CODE_NET_ACK will be sent later when the receive completes.
			sender->args.ms.srcmsglen = net_srcmsglen;
			SETKSTATUS(act, 0);
		}
		sender->state = STATE_SEND;
		snap_time(&sender->timestamp_last_block,0);
		_TRACE_TH_EMIT_STATE(sender, SEND);
	} else {
		//
		// Don't allow any MsgSend's to processes that are dying.
		// Only have to check here because of code in nano_signal.c
		// - check the comment where we turn on the _NTO_PF_COREDUMP
		// flag.
		//
		if(chp->process->flags & (_NTO_PF_TERMING | _NTO_PF_ZOMBIE | _NTO_PF_COREDUMP)) {
			return ENXIO;
		}
		// Can't use block(), because 'sender' might not actually be the
		// actives[KERNCPU] anymore...
		unready(sender, STATE_SEND);
	}

	sender->blocked_on = cop;
	pril_add(&chp->send_queue, sender);
	++cop->links;

	// To prevent priority inversion, boost all threads in the server
	//
	// for non-APS scheduling: raise prio of thread who last used this channel to at least that of the sender
	//
	// for APS scheduling: also cause the out-of-budget threads to inherit the budget of the sender,
	// but do not inherit the critical state.
	if((chp->flags & _NTO_CHF_FIXED_PRIORITY) == 0) {
		int i;

		for(i = 0 ; i < chp->process->threads.nentries ; ++i) {
			if(VECP(thp, &chp->process->threads, i) &&  thp->last_chid == chp->chid) {
				short may_run = may_thread_run(thp);
				if ( thp->priority < sender->priority ) {
					adjust_priority(thp, sender->priority, may_run ? thp->dpp : sender->dpp, 1 );
					thp->real_priority = thp->priority;
				} else {
					if (!may_run) {
						// server threads are higher prio, but have no budget. So inherit budget only
						adjust_priority(thp, thp->priority, sender->dpp, 1);
					}
				}
			}
		}
	}

	return ENOERROR;

send_fault:
	sender->flags &= ~_NTO_TF_BUFF_MSG;

	return EFAULT;

rcv_fault:
	sender->flags &= ~_NTO_TF_BUFF_MSG;
	kererr(thp, EFAULT);
	LINKPRIL_REM(thp);
	ready(thp);

	/* Restart the kernel call - same behavior as receive path */
	KERCALL_RESTART(act);

	return ENOERROR;
}
Exemple #13
0
int kdecl
ker_msg_replyv(THREAD *act, struct kerargs_msg_replyv *kap) {
	CONNECT			*cop;
	THREAD			*thp;
	int				 xferstat, status;
	unsigned		 flags = 0;

	// Future: Think about an inline version of this for speed
	if((cop = lookup_rcvid((KERARGS *)(void *)kap, kap->rcvid, &thp)) == NULL) {
		return ENOERROR;
	}

	// Make sure thread is replied blocked on a channel owned by this process.
	if(thp->state != STATE_REPLY && thp->state != STATE_NET_REPLY) {
		return ESRCH;
	}

	// See comment in ker_msg_error about ChannelDestroy handling.  This is a placeholder to
	// make sure that no-one changes qnet to call MsgReply rather than MsgError
	CRASHCHECK(_TRACE_GETSYSCALL(thp->syscall) == __KER_CHANNEL_DESTROY);

	// Verify that the message has been fully received, and that the receiving
	// thread has completed any specialret() processing that needs to be done.
	if(thp->internal_flags & _NTO_ITF_SPECRET_PENDING) {
		return ESRCH;
	}

	if(thp->blocked_on != cop) {
		CONNECT *cop1 = cop;
		cop = thp->blocked_on;
		if((cop->flags & COF_VCONNECT) == 0 || cop->un.lcl.cop != cop1) {
			return ESRCH;
		}
	}

	if(thp->internal_flags & _NTO_ITF_UNBLOCK_QUEUED) {
		remove_unblock(thp, cop, kap->rcvid);
		// no need to keep kernel locked after the unblock pulse is removed
		if(get_inkernel() & INKERNEL_LOCK) {
			unlock_kernel();
			KER_PREEMPT(act, ENOERROR);
		}
	}

	thp->flags &= ~(_NTO_TF_BUFF_MSG | _NTO_TF_SHORT_MSG);

	if(kap->sparts != 0) {
		/* Reply IOVs */
		if(kap->sparts < 0) {
			// Single part -- do the boundary check and copy if short message
			uintptr_t base, last;
			int	len;

			base = (uintptr_t) kap->smsg;
			len = -kap->sparts;
			last = base + len - 1;
			if((base > last) || !WITHIN_BOUNDRY(base, last, act->process->boundry_addr)) {
				// We know length is non-zero from test above
				xferstat = XFER_SRC_FAULT;
				goto xfer_err;
			}
			if(len <= sizeof(thp->args.msbuff.buff)) {
				 if((xferstat = xfer_memcpy(thp->args.msbuff.buff, (char *)base, thp->args.msbuff.msglen = len))) {
				 	goto xfer_err;
				 }
				flags = _NTO_TF_BUFF_MSG;
			}
		} else if(kap->sparts == 1) {
			// Single IOV -- do the boundary check and copy if short message
			uintptr_t base, last, len;

			base = (uintptr_t)GETIOVBASE(kap->smsg);
			len = GETIOVLEN(kap->smsg);
			last = base + len - 1;
			if(((base > last) || !WITHIN_BOUNDRY(base, last, act->process->boundry_addr)) && (len != 0)) {
				xferstat = XFER_SRC_FAULT;
				goto xfer_err;
			}
			if(len <= sizeof(thp->args.msbuff.buff)) {
				if((xferstat = xfer_memcpy(thp->args.msbuff.buff, (char *)base, thp->args.ms.msglen = len))) {
					goto xfer_err;
				}
				flags = _NTO_TF_BUFF_MSG;
			}
		} else {
			// Multi IOV case
			int len = 0;
			int len_last = 0;
			IOV *iov = kap->smsg;
			int sparts = kap->sparts;

			// Calculate reply length -- even if not requested, it is almost free
			// Also do boundary check
			while(sparts) {
				uintptr_t base, last;

				len += GETIOVLEN(iov);
				if (len <len_last ) {
					/*overflow. excessively long user IOV, possibly overlayed. pr62575 */
					xferstat = XFER_SRC_FAULT;
					goto xfer_err;
				}
				len_last = len;
				base = (uintptr_t)GETIOVBASE(iov);
				last = base + GETIOVLEN(iov) - 1;
				if(((base > last) || !WITHIN_BOUNDRY(base, last, act->process->boundry_addr)) && (GETIOVLEN(iov) != 0)) {
					xferstat = XFER_SRC_FAULT;
					goto xfer_err;
				}
				++iov;
				--sparts;
				// Keep copy of IOV -- NYI, only really need if no receiver
				//if(niov < _NUM_CACHED_SEND_IOV) {
				//	act->args.ms.siov[niov] = *iov;
				//}
			}
			if(len <= sizeof(thp->args.msbuff.buff)) {
				int pos = 0;
				iov = kap->smsg;
				sparts = kap->sparts;
				// Multi-IOV incoming message that is short
				// Optimization -- need memcpy_siov for efficiency
				while(sparts) {
					if((xferstat = xfer_memcpy(&thp->args.msbuff.buff[pos], GETIOVBASE(iov), GETIOVLEN(iov)))) {
						goto xfer_err;
					}

					pos += GETIOVLEN(iov);
					iov++;
					sparts--;
				}
				thp->args.ms.msglen = len;
				flags = _NTO_TF_BUFF_MSG;
			}
		}

		/*
		 * Up-front work is now done. There are a few things set:
		 *  - incoming reply IOVs are verified for boundary
		 *  - if short message, it has been copied into thp's short buffer
		 *  - flags is either zero (long message) or _NTO_TF_BUFF_MSG if short
		 */

		// This is a long message
		if(flags == 0) {
			act->args.ms.smsg = kap->smsg;
			act->args.ms.sparts = kap->sparts;
			START_SMP_XFER(act, thp);
			// Yes. Transfer the data.
			xferstat = xfermsg(thp, act, 0, 0);
			lock_kernel();
			END_SMP_XFER(act, thp);

#if defined(VARIANT_smp) && defined(SMP_MSGOPT)
			if(thp->internal_flags & _NTO_ITF_MSG_FORCE_RDY) {
				_TRACE_COMM_EMIT_REPLY(thp, cop, thp->tid+1);
				/* it could be channel destroy */
				force_ready(thp,KSTATUS(thp));
				thp->internal_flags &= ~_NTO_ITF_MSG_FORCE_RDY;
				KERCALL_RESTART(act);
				act->restart = 0;
				return ENOERROR;
			}
#endif
		} else {

			// Short message. We only do something if there is no target aspace --
			// reply to proc case. Else, this is handled in specret.
			if(thp->aspace_prp == NULL) {
				xferstat = xfer_cpy_diov(thp, thp->args.ri.rmsg, thp->args.msbuff.buff, thp->args.ri.rparts, thp->args.msbuff.msglen);
				lock_kernel();
			} else {
				xferstat = 0;
				lock_kernel();
				thp->blocked_on = thp;
				thp->flags |= (_NTO_TF_BUFF_MSG | _NTO_TF_SHORT_MSG);
			}
		}
		_TRACE_COMM_EMIT_REPLY(thp, cop, thp->tid+1);
	} else { // Zero-length reply -- common case
		xferstat = 0;
		lock_kernel();
		_TRACE_COMM_EMIT_REPLY(thp, cop, thp->tid+1);
	}


	thp->flags &= ~_NTO_TF_UNBLOCK_REQ;

	if(thp->args.ms.server != 0) {
		thp->args.ms.server->client = 0;
		thp->args.ms.server = 0;
	}

	if(xferstat) goto xfer_err;

	thp->restart = act->restart = NULL;

	LINKPRIL_REM(thp);
	if(--cop->links == 0) {
		connect_detach(cop, thp->priority);
	}

	ready(thp);
	SETKSTATUS(thp, kap->status);

	return EOK;

xfer_err:

	// Fault -- determine if this is sender or replyer.
	// If replier faulted let him know and abort the operation
	// and wakup the sender with an error.
	status = (xferstat & XFER_SRC_FAULT) ? ESRVRFAULT : EFAULT;
	kererr(thp, status);
	LINKPRIL_REM(thp);
	thp->flags &= ~(_NTO_TF_BUFF_MSG | _NTO_TF_SHORT_MSG);

	if(--cop->links == 0) {
		connect_detach(cop, thp->priority);
	}
	ready(thp);

	return status;
}