patch-2.1.23 linux/arch/sparc/ap1000/aplib.c
Next file: linux/arch/sparc/ap1000/apmmu.c
Previous file: linux/arch/sparc/ap1000/apinline.h
Back to the patch index
Back to the overall index
- Lines: 497
- Date:
Sun Jan 26 12:07:06 1997
- Orig file:
v2.1.22/linux/arch/sparc/ap1000/aplib.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v2.1.22/linux/arch/sparc/ap1000/aplib.c linux/arch/sparc/ap1000/aplib.c
@@ -0,0 +1,496 @@
+ /*
+ * Copyright 1996 The Australian National University.
+ * Copyright 1996 Fujitsu Laboratories Limited
+ *
+ * This software may be distributed under the terms of the Gnu
+ * Public License version 2 or later
+ */
+
+/* kernel based aplib.
+
+ This was initially implemented in user space, but we eventually
+ relented when we discovered some really nasty MSC hardware bugs and
+ decided to disallow access to the device registers by users. Pity :-(
+
+ Andrew Tridgell, November 1996
+*/
+
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <asm/page.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+ #include <asm/ap1000/pgtapmmu.h>
+#include <asm/ap1000/apreg.h>
+#include <asm/ap1000/apservice.h>
+#include <asm/ap1000/aplib.h>
+
+
+extern int *tnet_rel_cid_table;
+extern unsigned _cid, _ncel, _ncelx, _ncely, _cidx, _cidy;
+
+
+/* this is used to stop the task hogging the MSC while paging in data */
+static inline void page_in(char *addr,long size)
+{
+ unsigned sum = 0;
+ while (size > 0) {
+ sum += *(volatile char *)addr;
+ addr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+}
+
+
+/* this sets up the aplib structures using info passed in from user space
+ it should only be called once, and should be the first aplib call
+ it should be followed by APLIB_SYNC
+ */
+static inline int aplib_init(struct aplib_init *init)
+{
+ struct aplib_struct *aplib;
+ int error,i;
+ int old_uid;
+
+ error = verify_area(VERIFY_READ,init,sizeof(*init));
+ if (error) return error;
+ error = verify_area(VERIFY_READ,init->phys_cells,
+ sizeof(int)*init->numcells);
+ if (error) return error;
+ error = verify_area(VERIFY_WRITE,
+ init->ringbuffer,
+ init->ringbuf_size * sizeof(int));
+ if (error) return error;
+ error = verify_area(VERIFY_WRITE,
+ (char *)APLIB_PAGE_BASE,
+ APLIB_PAGE_LEN);
+ if (error) return error;
+
+ if (!MPP_IS_PAR_TASK(current->taskid))
+ return -EINVAL;
+
+ if (current->aplib)
+ return -EINVAL;
+
+ aplib = current->aplib = (struct aplib_struct *)APLIB_PAGE_BASE;
+
+ /* lock the aplib structure in memory */
+ old_uid = current->euid;
+ current->euid = 0;
+ memset(aplib,0,APLIB_PAGE_LEN);
+ error = sys_mlock(aplib,APLIB_PAGE_LEN);
+ current->euid = old_uid;
+ if (error) {
+ printk("mlock1 failed\n");
+ return error;
+ }
+
+ /* lock the ringbuffer in memory */
+ old_uid = current->euid;
+ current->euid = 0;
+ memset(init->ringbuffer,0,init->ringbuf_size*4);
+ error = sys_mlock(init->ringbuffer,init->ringbuf_size*4);
+ current->euid = old_uid;
+ if (error) {
+ printk("mlock2 failed\n");
+ return error;
+ }
+
+ aplib->ringbuf = init->ringbuffer;
+ aplib->ringbuf_size = init->ringbuf_size;
+ aplib->numcells = init->numcells;
+ aplib->cid = init->cid;
+ aplib->tid = current->taskid;
+ aplib->numcells_x = init->numcells_x;
+ aplib->numcells_y = init->numcells_y;
+ aplib->cidx = init->cid % init->numcells_x;
+ aplib->cidy = init->cid / init->numcells_x;
+
+ aplib->physical_cid = (unsigned *)(aplib+1);
+ aplib->rel_cid = aplib->physical_cid + init->numcells;
+
+ if ((char *)(aplib->rel_cid + init->numcells) >
+ (char *)(APLIB_PAGE_BASE + APLIB_PAGE_LEN)) {
+ return -ENOMEM;
+ }
+
+ memcpy(aplib->physical_cid,init->phys_cells,
+ sizeof(int)*init->numcells);
+
+ /* initialise the relative cid table */
+ for (i=0;i<aplib->numcells;i++)
+ aplib->rel_cid[i] =
+ tnet_rel_cid_table[aplib->physical_cid[i]];
+
+ return 0;
+}
+
+
+/* n == which sync line (ignored)
+ returns logical or of the stat values across the cells (1 bit resolution)
+
+ This has to be done very carefully as the tasks can startup on the cells
+ in any order, so we don't know which tasks have started up when this
+ is called
+*/
+static inline int aplib_sync(int n,int stat)
+{
+ struct aplib_struct *aplib = current->aplib;
+ static int sync_flags[MPP_NUM_TASKS];
+ int i,err;
+ int tsk = current->taskid;
+
+ stat &= 1;
+
+ if (aplib->numcells < 2)
+ return stat;
+
+ tsk -= MPP_TASK_BASE;
+
+ if (aplib->cid == 0) {
+ if ((err=wait_on_int(&sync_flags[tsk],
+ aplib->numcells-1,5)))
+ return err;
+ sync_flags[tsk] = 0;
+ if (aplib->numcells == _ncel) {
+ ap_bput(0,0,0,(u_long)&sync_flags[tsk],0);
+ } else {
+ for (i=1;i<aplib->numcells;i++)
+ ap_put(aplib->physical_cid[i],
+ 0,0,0,(u_long)&sync_flags[tsk],0);
+ }
+ } else {
+ ap_put(aplib->physical_cid[0],
+ 0,0,0,(u_long)&sync_flags[tsk],0);
+ if ((err=wait_on_int(&sync_flags[tsk],1,5)))
+ return err;
+ sync_flags[tsk] = 0;
+ }
+
+ /* I haven't written the xy_ calls yet ... */
+ /* aplib_xy_ior(stat,&stat); */
+
+ return stat;
+}
+
+
+
+static inline void _putget(unsigned q,
+ unsigned rcell,
+ unsigned *src_addr,
+ unsigned size,unsigned *dest_addr,
+ unsigned *dest_flag,unsigned *src_flag)
+{
+ unsigned flags;
+ volatile unsigned *entry = (volatile unsigned *)q;
+
+ save_flags(flags); cli();
+
+ *entry = rcell;
+ *entry = size;
+ *entry = (unsigned)dest_addr;
+ *entry = 0;
+ *entry = (unsigned)dest_flag;
+ *entry = (unsigned)src_flag;
+ *entry = (unsigned)src_addr;
+ *entry = 0;
+
+ restore_flags(flags);
+}
+
+
+/* a basic put() operation. Note the avoidance of odd word boundaries
+ and transfers sizes beyond what the hardware can deal with */
+static inline int aplib_put(struct aplib_putget *put)
+{
+ int error;
+ struct aplib_struct *aplib = current->aplib;
+
+ error = verify_area(VERIFY_WRITE,put,sizeof(*put));
+ if (error) return error;
+
+ if (put->cid >= aplib->numcells)
+ return -EINVAL;
+
+ do {
+ int n;
+
+ if (put->size && (((unsigned)put->src_addr) & 4)) {
+ n = 1;
+ } else if (put->size > MAX_PUT_SIZE) {
+ n = MAX_PUT_SIZE;
+ } else {
+ n = put->size;
+ }
+
+ put->size -= n;
+
+ page_in((char *)put->src_addr,n<<2);
+
+ _putget(MSC_PUT_QUEUE,
+ aplib->rel_cid[put->cid],
+ put->src_addr,
+ n,
+ put->dest_addr,
+ put->size?0:put->dest_flag,
+ put->size?0:put->src_flag);
+
+ put->dest_addr += n;
+ put->src_addr += n;
+ } while (put->size);
+
+ if (put->ack) {
+ aplib->ack_request++;
+ _putget(MSC_GET_QUEUE,
+ aplib->rel_cid[put->cid],
+ 0, 0, 0,
+ &aplib->ack_flag, 0);
+ }
+
+ return 0;
+}
+
+
+/* a basic get() operation */
+static inline int aplib_get(struct aplib_putget *get)
+{
+ struct aplib_struct *aplib = current->aplib;
+ int error = verify_area(VERIFY_WRITE,get,sizeof(*get));
+ if (error) return error;
+
+ if (get->cid >= aplib->numcells)
+ return -EINVAL;
+
+ do {
+ int n;
+
+ if (get->size && (((unsigned)get->src_addr) & 4)) {
+ n = 1;
+ } else if (get->size > MAX_PUT_SIZE) {
+ n = MAX_PUT_SIZE;
+ } else {
+ n = get->size;
+ }
+
+ get->size -= n;
+
+ page_in((char *)get->dest_addr,n<<2);
+
+ _putget(MSC_GET_QUEUE,
+ aplib->rel_cid[get->cid],
+ get->src_addr,
+ n,
+ get->dest_addr,
+ get->size?0:get->dest_flag,
+ get->size?0:get->src_flag);
+
+ get->dest_addr += n;
+ get->src_addr += n;
+ } while (get->size);
+
+ return 0;
+}
+
+
+/* we have received a protocol message - now do the get
+ This function is called from interrupt level with interrupts
+ disabled
+
+ note that send->size is now in words
+*/
+void aplib_bigrecv(unsigned *msgp)
+{
+ struct aplib_struct *aplib;
+ struct aplib_send *send = (struct aplib_send *)(msgp+2);
+ unsigned tid = (msgp[1]&0x3FF);
+ unsigned cid = (msgp[0]>>22)&0x1FF;
+ unsigned octx, ctx;
+ struct task_struct *tsk;
+ unsigned room;
+
+ tsk = task[tid];
+ if (!tsk || !tsk->aplib)
+ return;
+
+ octx = apmmu_get_context();
+ ctx = MPP_TASK_TO_CTX(tid);
+ if (octx != ctx)
+ apmmu_set_context(ctx);
+ aplib = tsk->aplib;
+
+ if (aplib->write_pointer < aplib->read_pointer)
+ room = aplib->read_pointer - (aplib->write_pointer+1);
+ else
+ room = aplib->ringbuf_size -
+ ((aplib->write_pointer+1)-aplib->read_pointer);
+
+ if (room < (send->size+2)) {
+ send_sig(SIGLOST,tsk,1);
+ goto finished;
+ }
+
+ aplib->ringbuf[aplib->write_pointer++] = send->info1;
+ aplib->ringbuf[aplib->write_pointer++] = send->info2;
+
+ /* now finally do the get() */
+ _putget(MSC_GET_QUEUE,
+ aplib->rel_cid[cid],
+ send->src_addr,
+ send->size,
+ &aplib->ringbuf[aplib->write_pointer],
+ &aplib->rbuf_flag2,
+ send->flag_addr);
+
+ aplib->write_pointer += send->size;
+ if (aplib->write_pointer >= aplib->ringbuf_size)
+ aplib->write_pointer -= aplib->ringbuf_size;
+
+finished:
+ if (octx != ctx)
+ apmmu_set_context(octx);
+}
+
+
+/* note the 8 byte alignment fix for the MSC bug */
+static inline int aplib_send(struct aplib_send *send)
+{
+ struct aplib_struct *aplib = current->aplib;
+ int wordSize;
+ int byteAlign, byteFix;
+ u_long src;
+ u_long info1, info2;
+ volatile unsigned *q = (volatile unsigned *)MSC_SEND_QUEUE_S;
+ extern long system_recv_flag;
+ int error;
+ unsigned flags, rcell;
+ unsigned flag_ptr;
+
+ error = verify_area(VERIFY_WRITE,send,sizeof(*send));
+ if (error) return error;
+
+ if (send->cid >= aplib->numcells)
+ return -EINVAL;
+
+ if (send->tag == RBUF_SYSTEM || send->tag == RBUF_BIGSEND)
+ return -EINVAL;
+
+ error = verify_area(VERIFY_READ,(char *)send->src_addr,send->size);
+ if (error) return error;
+
+ page_in((char *)send->src_addr,send->size);
+
+ rcell = aplib->rel_cid[send->cid];
+
+ byteAlign = send->src_addr & 0x3;
+ byteFix = send->size & 0x3;
+
+ wordSize = (send->size + byteAlign + 3) >> 2;
+
+ src = send->src_addr & ~3;
+
+ /* this handles the MSC alignment bug */
+ if (wordSize > 1 &&
+ (src & 4)) {
+ info1 |= 0x80000000;
+ src -= 4;
+ wordSize++;
+ }
+
+ info1 = (aplib->cid<<22) | (byteFix<<20) | wordSize;
+ info2 = (send->tag<<28) | (byteAlign<<26) |
+ (send->type<<10) | aplib->tid;
+ flag_ptr = (unsigned)&send->flag;
+
+ if (send->size > SMALL_SEND_THRESHOLD) {
+ send->info1 = info1;
+ send->info2 = info2;
+ send->size = wordSize;
+ send->src_addr = src;
+ send->flag_addr = (unsigned)&send->flag;
+ flag_ptr = 0;
+
+ wordSize = sizeof(*send)>>2;
+ src = (unsigned)send;
+
+ info1 = (aplib->cid<<22) | wordSize;
+ info2 = (RBUF_BIGSEND<<28) | aplib->tid;
+ }
+
+ save_flags(flags); cli();
+
+ *q = rcell;
+ *q = wordSize;
+ *q = (u_long)&system_recv_flag;
+ *q = flag_ptr;
+ *q = (u_long)src;
+ *q = 0;
+ *q = info1;
+ *q = info2;
+
+ restore_flags(flags);
+
+ return 0;
+}
+
+
+static inline int aplib_probe(void)
+{
+ tnet_check_completion();
+ return 0;
+}
+
+static inline int aplib_poll(unsigned counter)
+{
+ struct aplib_struct *aplib = current->aplib;
+
+ while (counter == aplib->rbuf_flag1 + aplib->rbuf_flag2) {
+ tnet_check_completion();
+ if (need_resched) break;
+ if (current->signal & ~current->blocked) break;
+ }
+ return 0;
+}
+
+int sys_aplib(unsigned call,int a1,int a2,int a3,int a4)
+{
+
+ if (!current->aplib && call != APLIB_INIT)
+ return -EINVAL;
+
+ switch (call) {
+ case APLIB_INIT:
+ return aplib_init((struct aplib_init *)a1);
+
+ case APLIB_SYNC:
+ return aplib_sync(a1,a2);
+
+ case APLIB_PUT:
+ return aplib_put((struct aplib_putget *)a1);
+
+ case APLIB_GET:
+ return aplib_get((struct aplib_putget *)a1);
+
+ case APLIB_SEND:
+ return aplib_send((struct aplib_send *)a1);
+
+ case APLIB_PROBE:
+ return aplib_probe();
+
+ case APLIB_POLL:
+ return aplib_poll((unsigned)a1);
+ }
+
+ return -EINVAL;
+}
+
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov