1 /* $NetBSD: shmif_busops.c,v 1.3 2010/08/15 18:47:38 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Antti Kantee. All Rights Reserved. 5 * 6 * Development of this software was supported by The Nokia Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: shmif_busops.c,v 1.3 2010/08/15 18:47:38 pooka Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/atomic.h> 35 36 #include "shmifvar.h" 37 38 #ifndef _KERNEL 39 #include <assert.h> 40 #define KASSERT(a) assert(a) 41 #else 42 #include <rump/rumpuser.h> 43 #endif 44 45 #define LOCK_UNLOCKED 0 46 #define LOCK_LOCKED 1 47 #define LOCK_COOLDOWN 1001 48 49 /* 50 * This locking needs work and will misbehave severely if: 51 * 1) the backing memory has to be paged in 52 * 2) some lockholder exits while holding the lock 53 */ 54 void 55 shmif_lockbus(struct shmif_mem *busmem) 56 { 57 int i = 0; 58 59 while (__predict_false(atomic_cas_32(&busmem->shm_lock, 60 LOCK_UNLOCKED, LOCK_LOCKED) == LOCK_LOCKED)) { 61 if (__predict_false(++i > LOCK_COOLDOWN)) { 62 uint64_t sec, nsec; 63 int error; 64 65 sec = 0; 66 nsec = 1000*1000; /* 1ms */ 67 rumpuser_nanosleep(&sec, &nsec, &error); 68 i = 0; 69 } 70 continue; 71 } 72 membar_enter(); 73 } 74 75 void 76 shmif_unlockbus(struct shmif_mem *busmem) 77 { 78 unsigned int old; 79 80 membar_exit(); 81 old = atomic_swap_32(&busmem->shm_lock, LOCK_UNLOCKED); 82 KASSERT(old == LOCK_LOCKED); 83 } 84 85 uint32_t 86 shmif_advance(uint32_t oldoff, uint32_t delta) 87 { 88 uint32_t newoff; 89 90 newoff = oldoff + delta; 91 if (newoff >= BUSMEM_DATASIZE) 92 newoff -= (BUSMEM_DATASIZE); 93 return newoff; 94 95 } 96 97 uint32_t 98 shmif_busread(struct shmif_mem *busmem, void *dest, uint32_t off, size_t len, 99 bool *wrap) 100 { 101 size_t chunk; 102 103 KASSERT(len < (BUSMEM_DATASIZE) && off <= BUSMEM_DATASIZE); 104 chunk = MIN(len, BUSMEM_DATASIZE - off); 105 memcpy(dest, busmem->shm_data + off, chunk); 106 len -= chunk; 107 108 if (len == 0) 109 return off + chunk; 110 111 /* else, wraps around */ 112 off = 0; 113 *wrap = true; 114 115 /* finish reading */ 116 memcpy((uint8_t *)dest + chunk, busmem->shm_data + off, len); 117 return off + len; 118 } 119 120 void 121 shmif_advancefirst(struct shmif_mem *busmem, uint32_t off, size_t len) 122 { 123 124 while (off <= busmem->shm_first + sizeof(struct shmif_pkthdr) 125 && off+len > busmem->shm_first) { 126 DPRINTF(("advancefirst: old offset %d, ", busmem->shm_first)); 127 busmem->shm_first = shmif_nextpktoff(busmem, busmem->shm_first); 128 DPRINTF(("new offset: %d\n", busmem->shm_first)); 129 } 130 } 131 132 uint32_t 133 shmif_buswrite(struct shmif_mem *busmem, uint32_t off, void *data, size_t len, 134 bool *wrap) 135 { 136 size_t chunk; 137 138 KASSERT(len < (BUSMEM_DATASIZE) && off <= BUSMEM_DATASIZE); 139 140 chunk = MIN(len, BUSMEM_DATASIZE - off); 141 len -= chunk; 142 143 shmif_advancefirst(busmem, off, chunk + (len ? 1 : 0)); 144 145 memcpy(busmem->shm_data + off, data, chunk); 146 147 DPRINTF(("buswrite: wrote %d bytes to %d", chunk, off)); 148 149 if (len == 0) { 150 DPRINTF(("\n")); 151 return off + chunk; 152 } 153 154 DPRINTF((", wrapped bytes %d to 0\n", len)); 155 156 /* else, wraps around */ 157 off = 0; 158 *wrap = true; 159 160 shmif_advancefirst(busmem, off, len); 161 162 /* finish writing */ 163 memcpy(busmem->shm_data + off, (uint8_t *)data + chunk, len); 164 return off + len; 165 } 166 167 uint32_t 168 shmif_nextpktoff(struct shmif_mem *busmem, uint32_t oldoff) 169 { 170 struct shmif_pkthdr sp; 171 bool dummy; 172 173 shmif_busread(busmem, &sp, oldoff, sizeof(sp), &dummy); 174 KASSERT(sp.sp_len < BUSMEM_DATASIZE); 175 176 return shmif_advance(oldoff, sizeof(sp) + sp.sp_len); 177 } 178