| #include <stdlib.h> |
| |
| #include <parlib/common.h> |
| #include <parlib/assert.h> |
| #include <parlib/stdio.h> |
| #include <ros/syscall.h> |
| #include <ros/ring_syscall.h> |
| #include <ros/sysevent.h> |
| #include <parlib/arc.h> |
| #include <errno.h> |
| #include <parlib/arch/arch.h> |
| #include <sys/param.h> |
| #include <parlib/arch/atomic.h> |
| #include <parlib/vcore.h> |
| |
| syscall_desc_pool_t syscall_desc_pool; |
| async_desc_pool_t async_desc_pool; |
| async_desc_t* current_async_desc; |
| |
| struct arsc_channel global_ac; |
| |
| void init_arc(struct arsc_channel* ac) |
| { |
| // Set up the front ring for the general syscall ring |
| // and the back ring for the general sysevent ring |
| mcs_lock_init(&ac->aclock); |
| ac->ring_page = (syscall_sring_t*)sys_init_arsc(); |
| |
| FRONT_RING_INIT(&ac->sysfr, ac->ring_page, SYSCALLRINGSIZE); |
| //BACK_RING_INIT(&syseventbackring, &(__procdata.syseventring), SYSEVENTRINGSIZE); |
| //TODO: eventually rethink about desc pools, they are here but no longer |
| //necessary |
| POOL_INIT(&syscall_desc_pool, MAX_SYSCALLS); |
| POOL_INIT(&async_desc_pool, MAX_ASYNCCALLS); |
| } |
| |
| // Wait on all syscalls within this async call. TODO - timeout or something? |
| int waiton_group_call(async_desc_t* desc, async_rsp_t* rsp) |
| { |
| syscall_rsp_t syscall_rsp; |
| syscall_desc_t* d; |
| int retval = 0; |
| int err = 0; |
| if (!desc) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| while (!(TAILQ_EMPTY(&desc->syslist))) { |
| d = TAILQ_FIRST(&desc->syslist); |
| err = waiton_syscall(d); |
| // TODO: processing the retval out of rsp here. might be |
| // specific to the async call. do we want to accumulate? |
| // return any negative values? depends what we want from the |
| // return value, so we might have to pass in a function that is |
| // used to do the processing and pass the answer back out in |
| // rsp. |
| //rsp->retval += syscall_rsp.retval; // For example |
| retval = MIN(retval, err); |
| // remove from the list and free the syscall desc |
| TAILQ_REMOVE(&desc->syslist, d, next); |
| POOL_PUT(&syscall_desc_pool, d); |
| } |
| // run a cleanup function for this desc, if available |
| if (desc->cleanup) |
| desc->cleanup(desc->data); |
| // free the asynccall desc |
| POOL_PUT(&async_desc_pool, desc); |
| return err; |
| } |
| |
| // Finds a free async_desc_t, on which you can wait for a series of syscalls |
| async_desc_t* get_async_desc(void) |
| { |
| async_desc_t* desc = POOL_GET(&async_desc_pool); |
| if (desc) { |
| // Clear out any data that was in the old desc |
| memset(desc, 0, sizeof(*desc)); |
| TAILQ_INIT(&desc->syslist); |
| } |
| return desc; |
| } |
| |
| // Finds a free sys_desc_t, on which you can wait for a specific syscall, and |
| // binds it to the group desc. |
| syscall_desc_t* get_sys_desc(async_desc_t* desc) |
| { |
| syscall_desc_t* d = POOL_GET(&syscall_desc_pool); |
| if (d) { |
| // Clear out any data that was in the old desc |
| memset(d, 0, sizeof(*d)); |
| TAILQ_INSERT_TAIL(&desc->syslist, d, next); |
| } |
| return d; |
| } |
| |
| // Gets an async and a sys desc, with the sys bound to async. Also sets |
| // current_async_desc. This is meant as an easy wrapper when there is only one |
| // syscall for an async call. |
| int get_all_desc(async_desc_t** a_desc, syscall_desc_t** s_desc) |
| { |
| assert(a_desc && s_desc); |
| if ((current_async_desc = get_async_desc()) == NULL){ |
| errno = EBUSY; |
| return -1; |
| } |
| *a_desc = current_async_desc; |
| if ((*s_desc = get_sys_desc(current_async_desc))) |
| return 0; |
| // in case we could get an async, but not a syscall desc, then clean up. |
| POOL_PUT(&async_desc_pool, current_async_desc); |
| current_async_desc = NULL; |
| errno = EBUSY; |
| return -1; |
| } |
| |
| // This runs one syscall instead of a group. |
| |
| // TODO: right now there is one channel (remote), in the future, the caller |
| // may specify local which will cause it to give up the core to do the work. |
| // creation of additional remote channel also allows the caller to prioritize |
| // work, because the default policy for the kernel is to roundrobin between |
| // them. |
| int async_syscall(arsc_channel_t* chan, syscall_req_t* req, syscall_desc_t** desc_ptr2) |
| { |
| // Note that this assumes one global frontring (TODO) |
| // abort if there is no room for our request. ring size is currently |
| // 64. we could spin til it's free, but that could deadlock if this |
| // same thread is supposed to consume the requests it is waiting on |
| // later. |
| syscall_desc_t* desc = malloc(sizeof (syscall_desc_t)); |
| desc->channel = chan; |
| syscall_front_ring_t *fr = &(desc->channel->sysfr); |
| //TODO: can do it locklessly using CAS, but could change with local |
| //async calls |
| struct mcs_lock_qnode local_qn = {0}; |
| mcs_lock_lock(&(chan->aclock), &local_qn); |
| if (RING_FULL(fr)) { |
| errno = EBUSY; |
| return -1; |
| } |
| // req_prod_pvt comes in as the previously produced item. need to |
| // increment to the next available spot, which is the one we'll work on. |
| // at some point, we need to listen for the responses. |
| desc->idx = ++(fr->req_prod_pvt); |
| syscall_req_t* r = RING_GET_REQUEST(fr, desc->idx); |
| // CAS on the req->status perhaps |
| req->status = REQ_alloc; |
| |
| memcpy(r, req, sizeof(syscall_req_t)); |
| r->status = REQ_ready; |
| // push our updates to syscallfrontring.req_prod_pvt |
| // note: it is ok to push without protection since it is atomic and |
| // kernel won't process any requests until they are marked REQ_ready |
| // (also atomic) |
| RING_PUSH_REQUESTS(fr); |
| //cprintf("DEBUG: sring->req_prod: %d, sring->rsp_prod: %d\n", |
| mcs_lock_unlock(&desc->channel->aclock, &local_qn); |
| *desc_ptr2 = desc; |
| return 0; |
| } |
| // Default convinence wrapper before other method of posting calls are available |
| |
| syscall_desc_t* arc_call(long int num, ...) |
| { |
| va_list vl; |
| va_start(vl,num); |
| struct syscall *p_sysc = malloc(sizeof (struct syscall)); |
| syscall_desc_t* desc; |
| if (p_sysc == NULL) { |
| errno = ENOMEM; |
| return 0; |
| } |
| p_sysc->num = num; |
| p_sysc->arg0 = va_arg(vl,long int); |
| p_sysc->arg1 = va_arg(vl,long int); |
| p_sysc->arg2 = va_arg(vl,long int); |
| p_sysc->arg3 = va_arg(vl,long int); |
| p_sysc->arg4 = va_arg(vl,long int); |
| p_sysc->arg5 = va_arg(vl,long int); |
| va_end(vl); |
| syscall_req_t arc = {REQ_alloc,NULL, NULL, p_sysc}; |
| async_syscall(&SYS_CHANNEL, &arc, &desc); |
| printf ( "%d pushed at %p \n", desc); |
| return desc; |
| } |
| |
| // consider a timeout too |
| // Wait until arsc returns, caller provides rsp buffer. |
| // eventually change this to return ret_val, set errno |
| |
| // What if someone calls waiton the same desc several times? |
| int waiton_syscall(syscall_desc_t* desc) |
| { |
| int retval = 0; |
| if (desc == NULL || desc->channel == NULL){ |
| errno = EFAULT; |
| return -1; |
| } |
| // Make sure we were given a desc with a non-NULL frontring. This could |
| // happen if someone forgot to check the error code on the paired |
| // syscall. |
| syscall_front_ring_t *fr = &desc->channel->sysfr; |
| |
| if (!fr){ |
| errno = EFAULT; |
| return -1; |
| } |
| printf("waiting %d\n", vcore_id()); |
| syscall_rsp_t* rsp = RING_GET_RESPONSE(fr, desc->idx); |
| |
| // ignoring the ring push response from the kernel side now |
| while (atomic_read(&rsp->sc->flags) != SC_DONE) |
| cpu_relax(); |
| // memcpy(rsp, rsp_inring, sizeof(*rsp)); |
| |
| // run a cleanup function for this desc, if available |
| if (rsp->cleanup) |
| rsp->cleanup(rsp->data); |
| if (RSP_ERRNO(rsp)){ |
| errno = RSP_ERRNO(rsp); |
| retval = -1; |
| } else |
| retval = RSP_RESULT(rsp); |
| atomic_inc((atomic_t*) &(fr->rsp_cons)); |
| return retval; |
| } |
| |
| |