/* * make_seg() * Given a vas and a byte range, return a segment describing it. * * The byte range must occupy a single pset. On error, 0 is returned. */ struct seg * make_seg(struct vas *vas, void *buf, uint buflen) { struct pview *pv; struct seg *s; int x; /* * Invalid buf/buflen? */ if ((buf == 0) || (buflen == 0)) { return(0); } /* * Get new segment */ s = MALLOC(sizeof(struct seg), MT_SEG); /* * Find pview holding the starting address */ pv = find_pview(vas, buf); if (!pv) { FREE(s, MT_SEG); return(0); } /* * Make sure end lies within pview */ if (((char *)buf + buflen) > ((char *)pv->p_vaddr+ptob(pv->p_len))) { v_lock(&pv->p_set->p_lock, SPL0); FREE(s, MT_SEG); return(0); } /* * Duplicate view, record byte offset */ s->s_pview = *pv; ref_pset(pv->p_set); s->s_off = (char *)buf - (char *)pv->p_vaddr; s->s_len = buflen; /* * Trim off leading and trailing pages from the view */ pv = &s->s_pview; x = btop(s->s_off); pv->p_off += x; s->s_off -= ptob(x); pv->p_len = btorp(s->s_off + buflen); /* * Done with set */ v_lock(&pv->p_set->p_lock, SPL0); return(s); }
/* * shut_client() * Shut down a connection from a client to a server */ void shut_client(struct portref *pr, int sema_held) { struct sysmsg *sm; struct port *port; ulong refs; /* * Decrement the reference count, just return if * it's not zero yet. * TBD: consider compare-and-exchange */ p_lock_void(&pr->p_lock, SPL0); refs = pr->p_refs; pr->p_refs -= 1; v_lock(&pr->p_lock, SPL0_SAME); if (refs > 1) { if (sema_held) { v_sema(&pr->p_sema); } return; } /* * Get a system message */ sm = MALLOC(sizeof(struct sysmsg), MT_SYSMSG); sm->sm_sender = pr; sm->sm_op = M_DISCONNECT; sm->sm_arg = (long)pr; sm->sm_arg1 = sm->sm_nseg = 0; /* * If he's closed on us at the same time, no problem. */ p_lock_void(&pr->p_lock, SPL0); if (!(port = pr->p_port)) { v_lock(&pr->p_lock, SPL0_SAME); free_portref(pr); FREE(sm, MT_SYSMSG); return; } /* * Put disconnect message on port's queue. Flag that we've * sent our final message. He will clean up from here. */ pr->p_state = PS_CLOSING; queue_msg(port, sm, SPL0); if (!sema_held) { p_sema_v_lock(&pr->p_sema, PRIHI, &pr->p_lock); } else { v_lock(&pr->p_lock, SPL0_SAME); } }
bool Output::Play() { bool ret = true; ScopedLock v_lock(videoMutex); ScopedLock a_lock(audioMutex); AVCodecContext *avcc; if (videoTrack && videoTrack->stream && videofd > -1 && (avcc = videoTrack->stream->codec)) { videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type, videoTrack->ac3flags); videoWriter->Init(videofd, videoTrack->stream, player); if (dioctl(videofd, VIDEO_SET_ENCODING, videoWriter->GetVideoEncoding(avcc->codec_id)) || dioctl(videofd, VIDEO_PLAY, NULL)) ret = false; } if (audioTrack && audioTrack->stream && audiofd > -1 && (avcc = audioTrack->stream->codec)) { audioWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type, audioTrack->ac3flags); audioWriter->Init(audiofd, audioTrack->stream, player); audio_encoding_t audioEncoding = AUDIO_ENCODING_LPCMA; if (audioTrack->ac3flags != 6) audioEncoding = audioWriter->GetAudioEncoding(avcc->codec_id); if (dioctl(audiofd, AUDIO_SET_ENCODING, audioEncoding) || dioctl(audiofd, AUDIO_PLAY, NULL)) ret = false; } return ret; }
/* * msg_accept() * Accept a connection with ID "arg_tran" */ int msg_accept(long arg_tran) { struct portref *pr; int error; /* * Look up the transaction, sanity check it */ pr = tran_find(arg_tran); if (!pr) { return(-1); } if (pr->p_state != PS_OPENING) { /* * Our caller's hosed. He's not connecting. */ error = err(EINVAL); } else { /* * Flag success, wake him. */ pr->p_state = PS_IODONE; v_sema(&pr->p_iowait); error = 0; } v_lock(&pr->p_lock, SPL0); return(error); }
bool Output::SwitchVideo(AVStream *stream) { ScopedLock v_lock(videoMutex); if (stream == videoStream) { return true; } if (videofd > -1) { dioctl(videofd, VIDEO_STOP, NULL); ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL); } videoStream = stream; if (stream) { AVCodecContext *avcc = stream->codec; if (!avcc) { return false; } videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type); videoWriter->Init(videofd, videoStream, player); if (videofd > -1) { dioctl(videofd, VIDEO_SET_ENCODING, Writer::GetVideoEncoding(avcc->codec_id)); dioctl(videofd, VIDEO_PLAY, NULL); } } return true; }
bool Output::Flush() { bool ret = true; ScopedLock v_lock(videoMutex); ScopedLock a_lock(audioMutex); if (videofd > -1 && ioctl(videofd, VIDEO_FLUSH, NULL)) { ret = false; } if (audiofd > -1 && audioWriter) { // flush audio decoder AVPacket packet; packet.data = NULL; packet.size = 0; audioWriter->Write(&packet, 0); if (ioctl(audiofd, AUDIO_FLUSH, NULL)) { ret = false; } } return ret; }
bool Output::Play() { bool ret = true; ScopedLock v_lock(videoMutex); ScopedLock a_lock(audioMutex); AVCodecContext *avcc; if (videoStream && videofd > -1 && (avcc = videoStream->codec)) { videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type); videoWriter->Init(videofd, videoStream, player); if (dioctl(videofd, VIDEO_SET_ENCODING, videoWriter->GetVideoEncoding(avcc->codec_id)) || dioctl(videofd, VIDEO_PLAY, NULL)) { ret = false; } } if (audioStream && audiofd > -1 && (avcc = audioStream->codec)) { audioWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type); audioWriter->Init(audiofd, audioStream, player); if (dioctl(audiofd, AUDIO_SET_ENCODING, audioWriter->GetAudioEncoding(avcc->codec_id)) || dioctl(audiofd, AUDIO_PLAY, NULL)) { ret = false; } } return ret; }
/* * get() * Access buffer, interlocking with BG */ static void get(struct buf *b) { if (!(b->b_flags & B_BUSY)) { return; } p_lock(&b->b_lock); if (!(b->b_flags & B_BUSY)) { v_lock(&b->b_lock); return; } b->b_flags |= B_WANT; v_lock(&b->b_lock); mutex_thread(0); ASSERT_DEBUG(!(b->b_flags & (B_WANT|B_BUSY)), "get: still busy/wanted"); }
/* * _sync_buf() * Sync back buffer if dirty * * Write back the 1st sector, or the whole buffer, as appropriate */ static void _sync_buf(struct buf *b, int from_qio) { ASSERT_DEBUG(b->b_flags & (B_SEC0 | B_SECS), "sync_buf: not ref'ed"); /* * Skip it if not dirty */ if (!(b->b_flags & B_DIRTY)) { return; } /* * Do the I/O--whole buffer, or just 1st sector if that was * the only sector referenced. */ if (!from_qio) { get(b); } if (b->b_flags & B_SECS) { write_secs(b->b_start, b->b_data, b->b_nsec); } else { write_secs(b->b_start, b->b_data, 1); } p_lock(&b->b_lock); b->b_flags &= ~B_DIRTY; v_lock(&b->b_lock); /* * If there are possible handles, clear them too */ if (b->b_handles) { bzero(b->b_handles, b->b_nhandle * sizeof(void *)); } }
bool Output::Stop() { bool ret = true; ScopedLock v_lock(videoMutex); ScopedLock a_lock(audioMutex); if (videofd > -1) { ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL); /* set back to normal speed (end trickmodes) */ dioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY); if (dioctl(videofd, VIDEO_STOP, NULL)) ret = false; } if (audiofd > -1) { ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL); /* set back to normal speed (end trickmodes) */ dioctl(audiofd, AUDIO_SET_SPEED, DVB_SPEED_NORMAL_PLAY); if (dioctl(audiofd, AUDIO_STOP, NULL)) ret = false; } return ret; }
bool Output::Open() { ScopedLock v_lock(videoMutex); ScopedLock a_lock(audioMutex); if (videofd < 0) videofd = open(VIDEODEV, O_RDWR); if (videofd < 0) return false; ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL); dioctl(videofd, VIDEO_SELECT_SOURCE, (void *) VIDEO_SOURCE_MEMORY); dioctl(videofd, VIDEO_SET_STREAMTYPE, (void *) STREAM_TYPE_PROGRAM); dioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY); if (audiofd < 0) audiofd = open(AUDIODEV, O_RDWR); if (audiofd < 0) { close(videofd); videofd = -1; return false; } ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL); dioctl(audiofd, AUDIO_SELECT_SOURCE, (void *) AUDIO_SOURCE_MEMORY); dioctl(audiofd, AUDIO_SET_STREAMTYPE, (void *) STREAM_TYPE_PROGRAM); return true; }
void base_sink::proc_input() { deque<basic_buff_ptr> temp; while (m_open_flag) { { v_lock(write_lck, mtx_msg_queue); if (m_msg_queue.size() > 0) { temp.swap(m_msg_queue); } } for (auto& x : temp) { process_internal(x); } temp.clear(); chrono::system_clock::time_point tp = chrono::system_clock::now() + chrono::milliseconds(500); v_unique_lock(lck, mtx_input); m_cond_input.wait_for(lck, chrono::milliseconds(100)); if (!m_open_flag) { break; } } for (auto& x : m_msg_queue) { process_internal(x); } m_msg_queue.clear(); }
int msg_distribution::read_host_info(const string &file_name) { if (!file_name.empty()) { fsys::path s(file_name); v_lock(lk, s_host_mutex); if (fsys::exists(s)) { pugi::xml_document doc; pugi::xml_parse_result xml_ret = doc.load_file(file_name.c_str()); if (pugi::xml_parse_status::status_ok == xml_ret.status) { pugi::xml_node host_nodes = doc.child("hosts"); if (!host_nodes.empty()) { for (pugi::xml_node_iterator item = host_nodes.begin(); item != host_nodes.end(); ++item) { string manuc = item->attribute("manufacture").as_string(); int port = item->attribute("port").as_int(); s_host_listen_lists.insert(port, manuc); } } /*for (pugi::xml_node_iterator item = doc.begin(); item != doc.end(); ++item) { string manuc = item->attribute("manufacture").as_string(); int port = item->attribute("port").as_int(); s_host_listen_lists.insert(port, manuc); }*/ return v_OK; } } } return v_ERR_BAD_ARGUMENT; }
/* * v_sema() * Leave semaphore */ void v_sema(struct sema *s) { int val; p_lock(&s->s_locked); val = (s->s_val += 1); v_lock(&s->s_locked); if (val <= 0) { (void)send(s->s_portmaster, FS_SEEK); } }
/* * close_client() * Dump a client when the server closes its port * * Returns 1 if it detects messages in the port's queue; no action * is taken on the portref. Otherwise zeroes the p_port field of * the portref, flagging that the server's gone. It also removes * the portref from the port's list, and finally returns 0. */ static int close_client(struct port *port, struct portref *pr) { int err; unmapsegs(&pr->p_segs); p_lock_void(&pr->p_lock, SPL0); p_lock_void(&port->p_lock, SPLHI); if (port->p_hd) { err = 1; } else { pr->p_port = 0; if (blocked_sema(&pr->p_iowait)) { v_sema(&pr->p_iowait); } deref_port(port, pr); err = 0; } v_lock(&port->p_lock, SPL0); v_lock(&pr->p_lock, SPL0_SAME); return(err); }
bool Output::Write(AVStream *stream, AVPacket *packet, int64_t pts) { switch (stream->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: { ScopedLock v_lock(videoMutex); return videofd > -1 && videoWriter && videoWriter->Write(packet, pts); } case AVMEDIA_TYPE_AUDIO: { ScopedLock a_lock(audioMutex); return audiofd > -1 && audioWriter && audioWriter->Write(packet, pts); } default: return false; } }
bool Output::Continue() { bool ret = true; ScopedLock v_lock(videoMutex); ScopedLock a_lock(audioMutex); if (videofd > -1 && dioctl(videofd, VIDEO_CONTINUE, NULL)) ret = false; if (audiofd > -1 && dioctl(audiofd, AUDIO_CONTINUE, NULL)) ret = false; return ret; }
/* * p_sema() * Enter semaphore */ int p_sema(struct sema *s) { int val; p_lock(&s->s_locked); val = (s->s_val -= 1); v_lock(&s->s_locked); if (val >= 0) { return(0); } if (send(s->s_port, FS_READ)) { return(-1); } return(0); }
bool Output::Pause() { bool ret = true; ScopedLock v_lock(videoMutex); ScopedLock a_lock(audioMutex); if (videofd > -1) { if (dioctl(videofd, VIDEO_FREEZE, NULL)) ret = false; } if (audiofd > -1) { if (dioctl(audiofd, AUDIO_PAUSE, NULL)) ret = false; } return ret; }
/* * bg_thread() * Endless loop to take QIO operations and execute them */ static void bg_thread(int dummy) { uint next = 0, want; struct qio *q; struct buf *b; /* * Become ephemeral */ (void)sched_op(SCHEDOP_EPHEM, 0); /* * Endless loop, serving background requests */ for (;;) { /* * Get next operation */ mutex_thread(0); q = &qios[next++]; if (next >= NQIO) { next = 0; } /* * Execute it */ exec_qio(b = q->q_buf, q->q_op); /* * Flag completion */ q->q_op = 0; p_lock(&b->b_lock); want = b->b_flags & B_WANT; b->b_flags &= ~(B_BUSY | B_WANT); v_lock(&b->b_lock); if (want) { mutex_thread(fg_pid); } } }
bool Output::Close() { Stop(); ScopedLock v_lock(videoMutex); ScopedLock a_lock(audioMutex); if (videofd > -1) { close(videofd); videofd = -1; } if (audiofd > -1) { close(audiofd); audiofd = -1; } videoTrack = NULL; audioTrack = NULL; return true; }
/* * msg_portname() * Tell server port name associated with portref */ port_name msg_portname(port_t arg_port) { struct portref *pr; port_name pn; /* * Access our open port reference. */ pr = find_portref(curthread->t_proc, arg_port); if (pr) { struct port *port; /* * It looked good. Take the portref spinlock so we * don't race with the server trying to exit. If we're * still connected to a server, get his port_name. */ port = pr->p_port; if (port) { pn = port->p_name; } else { /* * He bombed */ pn = err(EIO); } v_lock(&pr->p_lock, SPL0); v_sema(&pr->p_sema); } else { /* * Bad port requested. find_portref() sets err(). */ pn = -1; } return(pn); }
int base_sink::input(basic_buff_ptr buf) { v_lock(lck, mtx_msg_queue); m_msg_queue.push_back(buf); return v_OK; }
bool Output::ClearVideo() { ScopedLock v_lock(videoMutex); return videofd > -1 && !ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL); }
bool Output::SlowMotion(int speed) { ScopedLock v_lock(videoMutex); return videofd > -1 && !dioctl(videofd, VIDEO_SLOWMOTION, speed); }
bool Output::FastForward(int speed) { ScopedLock v_lock(videoMutex); return videofd > -1 && !dioctl(videofd, VIDEO_FAST_FORWARD, speed); }
/* * ptrace_slave() * Called by slave process when it detects a need to call the master * * Actually, it's called when it *appears* that a need exists; this * routine does locking and handles the case where it really wasn't * needed. * * XXX use "event" (pack into longs?), and allow them to clear it * unless it's the unblockable kill message. */ void ptrace_slave(char *event, uint why) { struct thread *t = curthread; struct proc *p = t->t_proc; struct portref *pr; port_t port; long args[3]; uint x; extern struct portref *find_portref(); retry: p_sema(&p->p_sema, PRIHI); /* * If we've raced with another thread doing the setup, just * continue. This would be a pretty chaotic situation anyway. */ if (p->p_dbg.pd_flags & PD_CONNECTING) { v_sema(&p->p_sema); return; } /* * If it appears we're the first to have seen this ptrace * request, do the initial connect and setup. Then start * over. */ if (p->p_dbg.pd_name && (p->p_dbg.pd_port == -1)) { ptrace_attach(); goto retry; } /* * Try to get our portref to the debug port. If it has * gone away, clear our debug environment and continue. * * After this block of code, we hold a semaphore for clients * on the named portref, and we have released our proc * semaphore. We are thus in a pretty good position to * interact at length with our debugger. */ port = p->p_dbg.pd_port; v_sema(&p->p_sema); pr = find_portref(p, port); if (pr == 0) { p_sema(&p->p_sema, PRIHI); /* * This could actually blow away a usable, new debug * session. C'est la vie. To get here you had to * hunt down your port and msg_disconnect() it yourself, * which is pretty hosed in itself. */ if (p->p_dbg.pd_port == port) { bzero(&p->p_dbg, sizeof(struct pdbg)); } v_sema(&p->p_sema); return; } /* * kernmsg_send() does this for itself. We hold the * semaphore, so we won't race with other I/O clients. * Since we have the lock, take this opportunity to * flag that this connection should never be dup'ed. */ pr->p_flags |= PF_NODUP; v_lock(&pr->p_lock, SPL0); /* * Return value is initially the bits which matched and * caused us to drop into ptrace_slave(). */ args[0] = why; args[1] = 0; for (;;) { /* * Build a message and send it */ if (kernmsg_send(pr, PD_SLAVE, args) < 0) { v_sema(&pr->p_sema); p_sema(&p->p_sema, PRIHI); (void)msg_disconnect(p->p_dbg.pd_port); bzero(&p->p_dbg, sizeof(struct pdbg)); v_sema(&p->p_sema); return; } /* * Act on his answer */ switch (args[2]) { case PD_RUN: /* Continue running */ v_sema(&pr->p_sema); return; case PD_STEP: /* Run for one step */ single_step(args[0]); break; case PD_BREAK: /* Set/clear breakpoint */ args[0] = set_break(args[1], args[0]); break; case PD_RDREG: /* Read register */ args[0] = getreg(args[0]); break; case PD_WRREG: /* Write register */ args[0] = setreg(args[0], args[1]); break; case PD_MASK: /* Set debug event mask */ p->p_dbg.pd_flags = args[0]; break; case PD_RDMEM: /* Read memory */ { ulong l; if (copyin((void *)args[0], &l, sizeof(l)) < 0) { args[1] = 1; } else { args[0] = l; args[1] = 0; } } break; case PD_WRMEM: /* Write memory */ if (copyout((void *)args[0], &args[1], sizeof(args[1]))) { args[1] = 1; } else { args[1] = 0; } args[0] = 0; break; case PD_MEVENT: /* Read/write event string */ x = args[0] & 0xFF; if (x > ERRLEN) { args[0] = -1; break; } if (args[0] & 0xFF00) { if (event) { event[x] = args[1]; } } else { if (event) { args[1] = event[x]; } else { args[1] = 0; } } break; case PD_PID: /* Tell him our PID/TID */ args[0] = p->p_pid; args[1] = t->t_pid; break; default: /* Bogus--drop him */ v_sema(&pr->p_sema); (void)msg_disconnect(port); p_sema(&p->p_sema, PRIHI); if (p->p_dbg.pd_port == port) { bzero(&p->p_dbg, sizeof(struct pdbg)); } v_sema(&p->p_sema); return; } } }
/* * vas_fault() * Process a fault within the given address space * * Returns 0 if the fault could be resolved, 1 if process needs to * receive an event. The HAT layer is expected to reliably hold * a translation added via hat_addtrans() until hat_deletetrans(). * A lost translation would cause the atl to hold multiple entries. */ vas_fault(void *vas, void *vaddr, int write) { struct pview *pv; struct pset *ps; struct perpage *pp; uint idx, pvidx; int error = 0; int wasvalid; /* * Easiest--no view matches address */ if ((pv = find_pview(vas, vaddr)) == 0) { return(1); } ASSERT_DEBUG(pv->p_valid, "vas_fault: pview !p_valid"); ps = pv->p_set; /* * Next easiest--trying to write to read-only view */ if (write && (pv->p_prot & PROT_RO)) { v_lock(&ps->p_lock, SPL0_SAME); return(1); } /* * Transfer from pset lock to page slot lock */ pvidx = btop((char *)vaddr - (char *)pv->p_vaddr); idx = pvidx + pv->p_off; pp = find_pp(ps, idx); lock_slot(ps, pp); /* * If the slot is bad, can't fill */ if (pp->pp_flags & PP_BAD) { error = 1; goto out; } /* * If slot is invalid, request it be filled. Otherwise just * add a reference. */ if (!(pp->pp_flags & PP_V)) { wasvalid = 0; if ((*(ps->p_ops->psop_fillslot))(ps, pp, idx)) { error = 1; goto out; } ASSERT(pp->pp_flags & PP_V, "vm_fault: lost the page"); } else { wasvalid = 1; ref_slot(ps, pp, idx); } /* * Break COW association when we write it */ if ((pp->pp_flags & PP_COW) && write) { /* * May or may not be there. If it is, remove * its reference from the per-page struct. */ if (wasvalid) { if (pv->p_valid[pvidx]) { ASSERT(delete_atl(pp, pv, pvidx) == 0, "vas_fault: p_valid no atl"); pv->p_valid[pvidx] = 0; } deref_slot(ps, pp, idx); } cow_write(ps, pp, idx); ASSERT(pp->pp_flags & PP_V, "vm_fault: lost the page 2"); /* * If not writing to a COW association, then inhibit adding * the translation if it's already present (another thread * ran and brought it in for us, probably) */ } else if (pv->p_valid[pvidx]) { deref_slot(ps, pp, idx); goto out; } /* * With a valid slot, add a hat translation and tabulate * the entry with an atl. */ add_atl(pp, pv, pvidx, 0); hat_addtrans(pv, vaddr, pp->pp_pfn, pv->p_prot | ((pp->pp_flags & PP_COW) ? PROT_RO : 0)); ASSERT_DEBUG(pv->p_valid[pvidx] == 0, "vas_fault: p_valid went on"); pv->p_valid[pvidx] = 1; /* * Free the various things we hold and return */ out: unlock_slot(ps, pp); return(error); }
/* * bounce_msgs() * Take the messages off a port, and bounce each of them * * We need to do this as sysmsg's can have segments within them, * and we need to clean those segments up. This routine is called * with the port locked, and returns with it released. */ static void bounce_msgs(struct port *port) { struct sysmsg *msgs, *sm, *smn; struct portref *pr; /* * Pull whatever messages are on the port out of the queue. * We'll walk the list once we've released our lock. We can't * hold the port lock and go for the portrefs, as we lock in * the other order, and would deadlock. */ p_lock_void(&port->p_lock, SPL0); msgs = port->p_hd; port->p_hd = 0; v_lock(&port->p_lock, SPL0); /* * Step through the linked list. As our clients will be waking * up and discarding messages, we record the next pointer field * into a local variable. */ for (sm = msgs; sm; sm = smn) { /* * Get pointers, apply sanity check */ pr = sm->sm_sender; smn = sm->sm_next; #ifdef DEBUG sm->sm_next = 0; #endif /* * ISR messages are somewhat special. They don't have * a portref, and there's no connection to break. * We have already de-registered, so it can't come * back. */ if (pr == 0) { ASSERT_DEBUG(sm->sm_op == M_ISR, "bounce_msgs: !pr !M_ISR"); continue; } ASSERT_DEBUG(pr->p_port == port, "bounce_msgs: msg in queue not for port"); /* * Lock portref, then port */ p_lock_void(&pr->p_lock, SPL0_SAME); p_lock_void(&port->p_lock, SPLHI); /* * If any segments in the message, discard them */ if (sm->sm_nseg > 0) { freesegs(sm); } /* * If the client has tried to abort the operation, * ignore anything but the abort message itself. */ if ((pr->p_state == PS_ABWAIT) && (sm->sm_op != M_ABORT)) { /* nothing */ ; } else { /* * Fill in sysmsg as an I/O error, clear * p_port to preclude further I/O, and * kick client awake. */ sm->sm_arg1 = sm->sm_arg = -1; strcpy(sm->sm_err, EIO); pr->p_port = 0; v_sema(&pr->p_iowait); } /* * Release locks, and remove the portref from our list */ v_lock(&port->p_lock, SPL0); v_lock(&pr->p_lock, SPL0_SAME); } }
/* * msg_err() * Return error reply to message * * Mostly because I didn't think it out too well, msg_err() returns * errors to both regular operations received with msg_receive(), as * well as connection requests. A connection is accepted with * msg_accept(), but rejected here. */ int msg_err(long arg_tran, const char *arg_why, int arg_len) { struct portref *pr; struct port *port; struct proc *p = curthread->t_proc; char errmsg[ERRLEN]; /* * Validate error string, copy it in. It's small, so we * just stash it on the stack. */ if ((arg_len < 1) || (arg_len >= ERRLEN)) { return(err(EINVAL)); } if (get_ustr(errmsg, ERRLEN, arg_why, arg_len)) { return(-1); } /* * Validate transaction ID. If we don't find it, this * system call returns an error immediately. For failed * opens, delete it from the hash before we release * the mutex. */ if (p_sema(&p->p_sema, PRICATCH)) { return(err(EINTR)); } pr = hash_lookup(p->p_prefs, arg_tran); if (pr) { p_lock_void(&pr->p_lock, SPL0); if (pr->p_state == PS_OPENING) { hash_delete(p->p_prefs, arg_tran); } } v_sema(&p->p_sema); if (!pr) { return(err(EINVAL)); } /* * If the server port's on its way down, never mind * this. If there's not a request outstanding from * this client, error that as well. */ port = pr->p_port; if (!port) { v_lock(&pr->p_lock, SPL0); return(err(EIO)); } if (pr->p_msg == 0) { v_lock(&pr->p_lock, SPL0); return(err(EINVAL)); } /* * Figure out if this is an open, an I/O, or if our caller's * just jerking us around. */ switch (pr->p_state) { case PS_IOWAIT: /* * The usual--he sent a request, and here's the error * response. Flag an error using the m_arg field of * his sysmsg; put the error string in the m_err part. * Wake him up. */ pr->p_state = PS_IODONE; pr->p_msg->sm_arg = -1; strcpy(pr->p_msg->sm_err, errmsg); v_sema(&pr->p_iowait); break; case PS_OPENING: /* * A failed open needs the portref cleared */ p_lock_void(&port->p_lock, SPLHI); deref_port(port, pr); v_lock(&port->p_lock, SPL0); /* * An open failed. We already deleted him from our * hash above. Flag open failure by setting his * p_state to anything but PS_IODONE. Wake him up. */ pr->p_state = PS_ABDONE; strcpy(pr->p_msg->sm_err, errmsg); v_sema(&pr->p_iowait); break; default: v_lock(&pr->p_lock, SPL0); return(err(EINVAL)); } v_lock(&pr->p_lock, SPL0); return(0); }