/* Close a socket, freeing it for reuse. Try to do a graceful close on a * TCP socket, if possible */ int close_s( int s /* Socket index */ ){ register struct usock *up; struct socklink *sp; if((up = itop(s)) == NULL){ errno = EBADF; return -1; } if(--up->refcnt > 0) return 0; /* Others are still using it */ /* Call proto-specific close routine if there is one */ if((sp = up->sp) != NULL && sp->close != NULL) (*sp->close)(up); free(up->name); free(up->peername); ksignal(up,0); /* Wake up anybody doing an accept() or recv() */ Usock[_fd_seq(up->index)] = NULL; free(up); return 0; }
/* Close down a socket three ways. Type 0 means "no more receives"; this * replaces the incoming data upcall with a routine that discards further * data. Type 1 means "no more sends", and obviously corresponds to sending * a TCP FIN. Type 2 means "no more receives or sends". This I interpret * as "abort the connection". */ int shutdown( int s, /* Socket index */ int how /* (see above) */ ){ register struct usock *up; struct socklink *sp; if((up = itop(s)) == NULL){ errno = EBADF; return -1; } if(up->cb.p == NULL){ errno = ENOTCONN; return -1; } sp = up->sp; /* Just close the socket if special shutdown routine not present */ if(sp->shut == NULL){ close_s(s); } else if((*sp->shut)(up,how) == -1){ return -1; } ksignal(up,0); return 0; }
/* TCP transmit upcall routine */ static void s_ttcall(struct tcb *tcb,int32 cnt) { /* Wake up anybody waiting to send data, and let them run */ ksignal(itop(tcb->user),1); kwait(NULL); }
int procfs_doctl(struct proc *curp, struct lwp *lp, struct pfsnode *pfs, struct uio *uio) { struct proc *p = lp->lwp_proc; int xlen; int error; char msg[PROCFS_CTLLEN+1]; vfs_namemap_t *nm; ASSERT_LWKT_TOKEN_HELD(&p->p_token); ASSERT_LWKT_TOKEN_HELD(&proc_token); if (uio->uio_rw != UIO_WRITE) return (EOPNOTSUPP); xlen = PROCFS_CTLLEN; error = vfs_getuserstr(uio, msg, &xlen); if (error) return (error); /* * Map signal names into signal generation * or debug control. Unknown commands and/or signals * return EOPNOTSUPP. * * Sending a signal while the process is being debugged * also has the side effect of letting the target continue * to run. There is no way to single-step a signal delivery. */ error = EOPNOTSUPP; nm = vfs_findname(ctlnames, msg, xlen); if (nm) { error = procfs_control(curp, lp, nm->nm_val); } else { nm = vfs_findname(signames, msg, xlen); if (nm) { if (TRACE_WAIT_P(curp, p)) { p->p_xstat = nm->nm_val; #ifdef FIX_SSTEP FIX_SSTEP(lp); #endif /* * Make the process runnable but do not * break its tsleep. */ proc_unstop(p); } else { ksignal(p, nm->nm_val); } error = 0; } } return (error); }
void shutdown_nice(int howto) { shutdown_howto = howto; /* Send a signal to init(8) and have it shutdown the world */ if (initproc != NULL) { ksignal(initproc, SIGINT); } else { /* No init(8) running, so simply reboot */ boot(RB_NOSYNC); } return; }
int sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag) { mouse_info_t *mouse; scr_stat *cur_scp; scr_stat *scp; struct proc *oproc; int f; scp = SC_STAT(tp->t_dev); switch (cmd) { case CONS_MOUSECTL: /* control mouse arrow */ mouse = (mouse_info_t*)data; cur_scp = scp->sc->cur_scp; switch (mouse->operation) { case MOUSE_MODE: if (ISSIGVALID(mouse->u.mode.signal)) { oproc = scp->mouse_proc; scp->mouse_signal = mouse->u.mode.signal; scp->mouse_proc = curproc; scp->mouse_pid = curproc->p_pid; PHOLD(curproc); } else { oproc = scp->mouse_proc; scp->mouse_signal = 0; scp->mouse_proc = NULL; scp->mouse_pid = 0; } if (oproc) { PRELE(oproc); oproc = NULL; } return 0; case MOUSE_SHOW: crit_enter(); if (!(scp->sc->flags & SC_MOUSE_ENABLED)) { scp->sc->flags |= SC_MOUSE_ENABLED; cur_scp->status &= ~MOUSE_HIDDEN; if (!ISGRAPHSC(cur_scp)) mark_all(cur_scp); crit_exit(); return 0; } else { crit_exit(); return EINVAL; } break; case MOUSE_HIDE: crit_enter(); if (scp->sc->flags & SC_MOUSE_ENABLED) { scp->sc->flags &= ~SC_MOUSE_ENABLED; sc_remove_all_mouse(scp->sc); crit_exit(); return 0; } else { crit_exit(); return EINVAL; } break; case MOUSE_MOVEABS: crit_enter(); scp->mouse_xpos = mouse->u.data.x; scp->mouse_ypos = mouse->u.data.y; set_mouse_pos(scp); crit_exit(); break; case MOUSE_MOVEREL: crit_enter(); scp->mouse_xpos += mouse->u.data.x; scp->mouse_ypos += mouse->u.data.y; set_mouse_pos(scp); crit_exit(); break; case MOUSE_GETINFO: mouse->u.data.x = scp->mouse_xpos; mouse->u.data.y = scp->mouse_ypos; mouse->u.data.z = 0; mouse->u.data.buttons = scp->mouse_buttons; return 0; case MOUSE_ACTION: case MOUSE_MOTION_EVENT: /* send out mouse event on /dev/sysmouse */ #if 0 /* this should maybe only be settable from /dev/consolectl SOS */ if (SC_VTY(tp->t_dev) != SC_CONSOLECTL) return ENOTTY; #endif crit_enter(); if (mouse->u.data.x != 0 || mouse->u.data.y != 0) { cur_scp->mouse_xpos += mouse->u.data.x; cur_scp->mouse_ypos += mouse->u.data.y; set_mouse_pos(cur_scp); } f = 0; if (mouse->operation == MOUSE_ACTION) { f = cur_scp->mouse_buttons ^ mouse->u.data.buttons; cur_scp->mouse_buttons = mouse->u.data.buttons; } crit_exit(); if (sysmouse_event(mouse) == 0) return 0; /* * If any buttons are down or the mouse has moved a lot, * stop the screen saver. */ if (((mouse->operation == MOUSE_ACTION) && mouse->u.data.buttons) || (mouse->u.data.x*mouse->u.data.x + mouse->u.data.y*mouse->u.data.y >= SC_WAKEUP_DELTA*SC_WAKEUP_DELTA)) { sc_touch_scrn_saver(); } cur_scp->status &= ~MOUSE_HIDDEN; if (cur_scp->mouse_signal) { /* has controlling process died? */ if (cur_scp->mouse_proc && (cur_scp->mouse_proc != pfindn(cur_scp->mouse_pid))){ oproc = cur_scp->mouse_proc; cur_scp->mouse_signal = 0; cur_scp->mouse_proc = NULL; cur_scp->mouse_pid = 0; if (oproc) PRELE(oproc); } else { ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal); break; } } if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL)) break; #ifndef SC_NO_CUTPASTE if ((mouse->operation == MOUSE_ACTION) && f) { /* process button presses */ if (cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN) mouse_cut_start(cur_scp); else mouse_cut_end(cur_scp); if (cur_scp->mouse_buttons & MOUSE_BUTTON2DOWN || cur_scp->mouse_buttons & MOUSE_BUTTON3DOWN) mouse_paste(cur_scp); } #endif /* SC_NO_CUTPASTE */ break; case MOUSE_BUTTON_EVENT: if ((mouse->u.event.id & MOUSE_BUTTONS) == 0) return EINVAL; if (mouse->u.event.value < 0) return EINVAL; #if 0 /* this should maybe only be settable from /dev/consolectl SOS */ if (SC_VTY(tp->t_dev) != SC_CONSOLECTL) return ENOTTY; #endif if (mouse->u.event.value > 0) cur_scp->mouse_buttons |= mouse->u.event.id; else cur_scp->mouse_buttons &= ~mouse->u.event.id; if (sysmouse_event(mouse) == 0) return 0; /* if a button is held down, stop the screen saver */ if (mouse->u.event.value > 0) sc_touch_scrn_saver(); cur_scp->status &= ~MOUSE_HIDDEN; if (cur_scp->mouse_signal) { if (cur_scp->mouse_proc && (cur_scp->mouse_proc != pfindn(cur_scp->mouse_pid))){ oproc = cur_scp->mouse_proc; cur_scp->mouse_signal = 0; cur_scp->mouse_proc = NULL; cur_scp->mouse_pid = 0; if (oproc) PRELE(oproc); } else { ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal); break; } } if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL)) break; #ifndef SC_NO_CUTPASTE switch (mouse->u.event.id) { case MOUSE_BUTTON1DOWN: switch (mouse->u.event.value % 4) { case 0: /* up */ mouse_cut_end(cur_scp); break; case 1: /* single click: start cut operation */ mouse_cut_start(cur_scp); break; case 2: /* double click: cut a word */ mouse_cut_word(cur_scp); mouse_cut_end(cur_scp); break; case 3: /* triple click: cut a line */ mouse_cut_line(cur_scp); mouse_cut_end(cur_scp); break; } break; case SC_MOUSE_PASTEBUTTON: switch (mouse->u.event.value) { case 0: /* up */ break; default: mouse_paste(cur_scp); break; } break; case SC_MOUSE_EXTENDBUTTON: switch (mouse->u.event.value) { case 0: /* up */ if (!(cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN)) mouse_cut_end(cur_scp); break; default: mouse_cut_extend(cur_scp); break; } break; } #endif /* SC_NO_CUTPASTE */ break; case MOUSE_MOUSECHAR: if (mouse->u.mouse_char < 0) { mouse->u.mouse_char = scp->sc->mouse_char; } else { if (mouse->u.mouse_char >= (unsigned char)-1 - 4) return EINVAL; crit_enter(); sc_remove_all_mouse(scp->sc); #ifndef SC_NO_FONT_LOADING if (ISTEXTSC(cur_scp) && (cur_scp->font != NULL)) sc_load_font(cur_scp, 0, cur_scp->font_size, cur_scp->font, cur_scp->sc->mouse_char, 4); #endif scp->sc->mouse_char = mouse->u.mouse_char; crit_exit(); } break; default: return EINVAL; } return 0; } return ENOIOCTL; }
static int tmpfs_write (struct vop_write_args *ap) { struct buf *bp; struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; struct thread *td = uio->uio_td; struct tmpfs_node *node; boolean_t extended; off_t oldsize; int error; off_t base_offset; size_t offset; size_t len; struct rlimit limit; int trivial = 0; int kflags = 0; int seqcount; error = 0; if (uio->uio_resid == 0) { return error; } node = VP_TO_TMPFS_NODE(vp); if (vp->v_type != VREG) return (EINVAL); seqcount = ap->a_ioflag >> 16; TMPFS_NODE_LOCK(node); oldsize = node->tn_size; if (ap->a_ioflag & IO_APPEND) uio->uio_offset = node->tn_size; /* * Check for illegal write offsets. */ if (uio->uio_offset + uio->uio_resid > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) { error = EFBIG; goto done; } /* * NOTE: Ignore if UIO does not come from a user thread (e.g. VN). */ if (vp->v_type == VREG && td != NULL && td->td_lwp != NULL) { error = kern_getrlimit(RLIMIT_FSIZE, &limit); if (error) goto done; if (uio->uio_offset + uio->uio_resid > limit.rlim_cur) { ksignal(td->td_proc, SIGXFSZ); error = EFBIG; goto done; } } /* * Extend the file's size if necessary */ extended = ((uio->uio_offset + uio->uio_resid) > node->tn_size); while (uio->uio_resid > 0) { /* * Don't completely blow out running buffer I/O * when being hit from the pageout daemon. */ if (uio->uio_segflg == UIO_NOCOPY && (ap->a_ioflag & IO_RECURSE) == 0) { bwillwrite(TMPFS_BLKSIZE); } /* * Use buffer cache I/O (via tmpfs_strategy) */ offset = (size_t)uio->uio_offset & TMPFS_BLKMASK64; base_offset = (off_t)uio->uio_offset - offset; len = TMPFS_BLKSIZE - offset; if (len > uio->uio_resid) len = uio->uio_resid; if ((uio->uio_offset + len) > node->tn_size) { trivial = (uio->uio_offset <= node->tn_size); error = tmpfs_reg_resize(vp, uio->uio_offset + len, trivial); if (error) break; } /* * Read to fill in any gaps. Theoretically we could * optimize this if the write covers the entire buffer * and is not a UIO_NOCOPY write, however this can lead * to a security violation exposing random kernel memory * (whatever junk was in the backing VM pages before). * * So just use bread() to do the right thing. */ error = bread(vp, base_offset, TMPFS_BLKSIZE, &bp); error = uiomovebp(bp, (char *)bp->b_data + offset, len, uio); if (error) { kprintf("tmpfs_write uiomove error %d\n", error); brelse(bp); break; } if (uio->uio_offset > node->tn_size) { node->tn_size = uio->uio_offset; kflags |= NOTE_EXTEND; } kflags |= NOTE_WRITE; /* * Always try to flush the page in the UIO_NOCOPY case. This * can come from the pageout daemon or during vnode eviction. * It is not necessarily going to be marked IO_ASYNC/IO_SYNC. * * For the normal case we buwrite(), dirtying the underlying * VM pages instead of dirtying the buffer and releasing the * buffer as a clean buffer. This allows tmpfs to use * essentially all available memory to cache file data. * If we used bdwrite() the buffer cache would wind up * flushing the data to swap too quickly. * * But because tmpfs can seriously load the VM system we * fall-back to using bdwrite() when free memory starts * to get low. This shifts the load away from the VM system * and makes tmpfs act more like a normal filesystem with * regards to disk activity. * * tmpfs pretty much fiddles directly with the VM * system, don't let it exhaust it or we won't play * nice with other processes. Only do this if the * VOP is coming from a normal read/write. The VM system * handles the case for UIO_NOCOPY. */ bp->b_flags |= B_CLUSTEROK; if (uio->uio_segflg == UIO_NOCOPY) { /* * Flush from the pageout daemon, deal with * potentially very heavy tmpfs write activity * causing long stalls in the pageout daemon * before pages get to free/cache. * * (a) Under severe pressure setting B_DIRECT will * cause a buffer release to try to free the * underlying pages. * * (b) Under modest memory pressure the B_RELBUF * alone is sufficient to get the pages moved * to the cache. We could also force this by * setting B_NOTMETA but that might have other * unintended side-effects (e.g. setting * PG_NOTMETA on the VM page). * * Hopefully this will unblock the VM system more * quickly under extreme tmpfs write load. */ if (vm_page_count_min(vm_page_free_hysteresis)) bp->b_flags |= B_DIRECT; bp->b_flags |= B_AGE | B_RELBUF; bp->b_act_count = 0; /* buffer->deactivate pgs */ cluster_awrite(bp); } else if (vm_page_count_target()) { /* * Normal (userland) write but we are low on memory, * run the buffer the buffer cache. */ bp->b_act_count = 0; /* buffer->deactivate pgs */ bdwrite(bp); } else { /* * Otherwise run the buffer directly through to the * backing VM store. */ buwrite(bp); /*vm_wait_nominal();*/ } if (bp->b_error) { kprintf("tmpfs_write bwrite error %d\n", bp->b_error); break; } } if (error) { if (extended) { (void)tmpfs_reg_resize(vp, oldsize, trivial); kflags &= ~NOTE_EXTEND; } goto done; } /* * Currently we don't set the mtime on files modified via mmap() * because we can't tell the difference between those modifications * and an attempt by the pageout daemon to flush tmpfs pages to * swap. * * This is because in order to defer flushes as long as possible * buwrite() works by marking the underlying VM pages dirty in * order to be able to dispose of the buffer cache buffer without * flushing it. */ if (uio->uio_segflg != UIO_NOCOPY) node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED; if (extended) node->tn_status |= TMPFS_NODE_CHANGED; if (node->tn_mode & (S_ISUID | S_ISGID)) { if (priv_check_cred(ap->a_cred, PRIV_VFS_RETAINSUGID, 0)) node->tn_mode &= ~(S_ISUID | S_ISGID); } done: TMPFS_NODE_UNLOCK(node); if (kflags) tmpfs_knote(vp, kflags); return(error); }
/* * Go through the rigmarole of shutting down.. * this used to be in machdep.c but I'll be dammned if I could see * anything machine dependant in it. */ static void boot(int howto) { /* * Get rid of any user scheduler baggage and then give * us a high priority. */ if (curthread->td_release) curthread->td_release(curthread); lwkt_setpri_self(TDPRI_MAX); /* collect extra flags that shutdown_nice might have set */ howto |= shutdown_howto; #ifdef SMP /* * We really want to shutdown on the BSP. Subsystems such as ACPI * can't power-down the box otherwise. */ if (smp_active_mask > 1) { kprintf("boot() called on cpu#%d\n", mycpu->gd_cpuid); } if (panicstr == NULL && mycpu->gd_cpuid != 0) { kprintf("Switching to cpu #0 for shutdown\n"); lwkt_setcpu_self(globaldata_find(0)); } #endif /* * Do any callouts that should be done BEFORE syncing the filesystems. */ EVENTHANDLER_INVOKE(shutdown_pre_sync, howto); /* * Try to get rid of any remaining FS references. The calling * process, proc0, and init may still hold references. The * VFS cache subsystem may still hold a root reference to root. * * XXX this needs work. We really need to SIGSTOP all remaining * processes in order to avoid blowups due to proc0's filesystem * references going away. For now just make sure that the init * process is stopped. */ if (panicstr == NULL) { shutdown_cleanup_proc(curproc); shutdown_cleanup_proc(&proc0); if (initproc) { if (initproc != curproc) { ksignal(initproc, SIGSTOP); tsleep(boot, 0, "shutdn", hz / 20); } shutdown_cleanup_proc(initproc); } vfs_cache_setroot(NULL, NULL); } /* * Now sync filesystems */ if (!cold && (howto & RB_NOSYNC) == 0 && waittime < 0) { int iter, nbusy, pbusy; waittime = 0; kprintf("\nsyncing disks... "); sys_sync(NULL); /* YYY was sync(&proc0, NULL). why proc0 ? */ /* * With soft updates, some buffers that are * written will be remarked as dirty until other * buffers are written. */ for (iter = pbusy = 0; iter < 20; iter++) { nbusy = scan_all_buffers(shutdown_busycount1, NULL); if (nbusy == 0) break; kprintf("%d ", nbusy); if (nbusy < pbusy) iter = 0; pbusy = nbusy; /* * XXX: * Process soft update work queue if buffers don't sync * after 6 iterations by permitting the syncer to run. */ if (iter > 5) bio_ops_sync(NULL); sys_sync(NULL); /* YYY was sync(&proc0, NULL). why proc0 ? */ tsleep(boot, 0, "shutdn", hz * iter / 20 + 1); } kprintf("\n"); /* * Count only busy local buffers to prevent forcing * a fsck if we're just a client of a wedged NFS server */ nbusy = scan_all_buffers(shutdown_busycount2, NULL); if (nbusy) { /* * Failed to sync all blocks. Indicate this and don't * unmount filesystems (thus forcing an fsck on reboot). */ kprintf("giving up on %d buffers\n", nbusy); #ifdef DDB if (debugger_on_panic) Debugger("busy buffer problem"); #endif /* DDB */ tsleep(boot, 0, "shutdn", hz * 5 + 1); } else { kprintf("done\n"); /* * Unmount filesystems */ if (panicstr == NULL) vfs_unmountall(); } tsleep(boot, 0, "shutdn", hz / 10 + 1); } print_uptime(); /* * Dump before doing post_sync shutdown ops */ crit_enter(); if ((howto & (RB_HALT|RB_DUMP)) == RB_DUMP && !cold) { dumpsys(); } /* * Ok, now do things that assume all filesystem activity has * been completed. This will also call the device shutdown * methods. */ EVENTHANDLER_INVOKE(shutdown_post_sync, howto); /* Now that we're going to really halt the system... */ EVENTHANDLER_INVOKE(shutdown_final, howto); for(;;) ; /* safety against shutdown_reset not working */ /* NOTREACHED */ }
/* * do an ioctl operation on a pfsnode (vp). * (vp) is not locked on entry or exit. */ static int linprocfs_ioctl(struct vop_ioctl_args *ap) { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *procp; int error; int signo; struct procfs_status *psp; unsigned char flags; procp = pfind(pfs->pfs_pid); if (procp == NULL) { return ENOTTY; } if (p_trespass(ap->a_cred, procp->p_ucred)) { error = EPERM; goto done; } switch (ap->a_command) { case PIOCBIS: procp->p_stops |= *(unsigned int*)ap->a_data; break; case PIOCBIC: procp->p_stops &= ~*(unsigned int*)ap->a_data; break; case PIOCSFL: /* * NFLAGS is "non-suser_xxx flags" -- currently, only * PFS_ISUGID ("ignore set u/g id"); */ #define NFLAGS (PF_ISUGID) flags = (unsigned char)*(unsigned int*)ap->a_data; if (flags & NFLAGS && (error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0))) goto done; procp->p_pfsflags = flags; break; case PIOCGFL: *(unsigned int*)ap->a_data = (unsigned int)procp->p_pfsflags; case PIOCSTATUS: psp = (struct procfs_status *)ap->a_data; psp->state = (procp->p_step == 0); psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; if (procp->p_step) { psp->why = procp->p_stype; psp->val = procp->p_xstat; } else { psp->why = psp->val = 0; /* Not defined values */ } break; case PIOCWAIT: psp = (struct procfs_status *)ap->a_data; if (procp->p_step == 0) { error = tsleep(&procp->p_stype, PCATCH, "piocwait", 0); if (error) goto done; } psp->state = 1; /* It stopped */ psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; psp->why = procp->p_stype; /* why it stopped */ psp->val = procp->p_xstat; /* any extra info */ break; case PIOCCONT: /* Restart a proc */ if (procp->p_step == 0) { error = EINVAL; /* Can only start a stopped process */ goto done; } if ((signo = *(int*)ap->a_data) != 0) { if (signo >= NSIG || signo <= 0) { error = EINVAL; goto done; } ksignal(procp, signo); } procp->p_step = 0; wakeup(&procp->p_step); break; default: error = ENOTTY; goto done; } error = 0; done: if (procp) PRELE(procp); return error; }
static int tmpfs_write (struct vop_write_args *ap) { struct buf *bp; struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; struct thread *td = uio->uio_td; struct tmpfs_node *node; boolean_t extended; off_t oldsize; int error; off_t base_offset; size_t offset; size_t len; struct rlimit limit; int trivial = 0; int kflags = 0; error = 0; if (uio->uio_resid == 0) { return error; } node = VP_TO_TMPFS_NODE(vp); if (vp->v_type != VREG) return (EINVAL); lwkt_gettoken(&vp->v_mount->mnt_token); oldsize = node->tn_size; if (ap->a_ioflag & IO_APPEND) uio->uio_offset = node->tn_size; /* * Check for illegal write offsets. */ if (uio->uio_offset + uio->uio_resid > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) { lwkt_reltoken(&vp->v_mount->mnt_token); return (EFBIG); } if (vp->v_type == VREG && td != NULL) { error = kern_getrlimit(RLIMIT_FSIZE, &limit); if (error != 0) { lwkt_reltoken(&vp->v_mount->mnt_token); return error; } if (uio->uio_offset + uio->uio_resid > limit.rlim_cur) { ksignal(td->td_proc, SIGXFSZ); lwkt_reltoken(&vp->v_mount->mnt_token); return (EFBIG); } } /* * Extend the file's size if necessary */ extended = ((uio->uio_offset + uio->uio_resid) > node->tn_size); while (uio->uio_resid > 0) { /* * Use buffer cache I/O (via tmpfs_strategy) */ offset = (size_t)uio->uio_offset & BMASK; base_offset = (off_t)uio->uio_offset - offset; len = BSIZE - offset; if (len > uio->uio_resid) len = uio->uio_resid; if ((uio->uio_offset + len) > node->tn_size) { trivial = (uio->uio_offset <= node->tn_size); error = tmpfs_reg_resize(vp, uio->uio_offset + len, trivial); if (error) break; } /* * Read to fill in any gaps. Theoretically we could * optimize this if the write covers the entire buffer * and is not a UIO_NOCOPY write, however this can lead * to a security violation exposing random kernel memory * (whatever junk was in the backing VM pages before). * * So just use bread() to do the right thing. */ error = bread(vp, base_offset, BSIZE, &bp); error = uiomovebp(bp, (char *)bp->b_data + offset, len, uio); if (error) { kprintf("tmpfs_write uiomove error %d\n", error); brelse(bp); break; } if (uio->uio_offset > node->tn_size) { node->tn_size = uio->uio_offset; kflags |= NOTE_EXTEND; } kflags |= NOTE_WRITE; /* * Always try to flush the page if the request is coming * from the pageout daemon (IO_ASYNC), else buwrite() the * buffer. * * buwrite() dirties the underlying VM pages instead of * dirtying the buffer, releasing the buffer as a clean * buffer. This allows tmpfs to use essentially all * available memory to cache file data. If we used bdwrite() * the buffer cache would wind up flushing the data to * swap too quickly. */ bp->b_flags |= B_AGE; if (ap->a_ioflag & IO_ASYNC) { bawrite(bp); } else { buwrite(bp); } if (bp->b_error) { kprintf("tmpfs_write bwrite error %d\n", bp->b_error); break; } } if (error) { if (extended) { (void)tmpfs_reg_resize(vp, oldsize, trivial); kflags &= ~NOTE_EXTEND; } goto done; } /* * Currently we don't set the mtime on files modified via mmap() * because we can't tell the difference between those modifications * and an attempt by the pageout daemon to flush tmpfs pages to * swap. * * This is because in order to defer flushes as long as possible * buwrite() works by marking the underlying VM pages dirty in * order to be able to dispose of the buffer cache buffer without * flushing it. */ TMPFS_NODE_LOCK(node); if (uio->uio_segflg != UIO_NOCOPY) node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED; if (extended) node->tn_status |= TMPFS_NODE_CHANGED; if (node->tn_mode & (S_ISUID | S_ISGID)) { if (priv_check_cred(ap->a_cred, PRIV_VFS_RETAINSUGID, 0)) node->tn_mode &= ~(S_ISUID | S_ISGID); } TMPFS_NODE_UNLOCK(node); done: tmpfs_knote(vp, kflags); lwkt_reltoken(&vp->v_mount->mnt_token); return(error); }
/* * Handle signals, upcalls, profiling, and other AST's and/or tasks that * must be completed before we can return to or try to return to userland. * * Note that td_sticks is a 64 bit quantity, but there's no point doing 64 * arithmatic on the delta calculation so the absolute tick values are * truncated to an integer. */ static void userret(struct lwp *lp, struct trapframe *frame, int sticks) { struct proc *p = lp->lwp_proc; int sig; int ptok; /* * Charge system time if profiling. Note: times are in microseconds. * This may do a copyout and block, so do it first even though it * means some system time will be charged as user time. */ if (p->p_flags & P_PROFIL) { addupc_task(p, frame->tf_rip, (u_int)((int)lp->lwp_thread->td_sticks - sticks)); } recheck: /* * Specific on-return-to-usermode checks (LWP_MP_WEXIT, * LWP_MP_VNLRU, etc). */ if (lp->lwp_mpflags & LWP_MP_URETMASK) lwpuserret(lp); /* * Block here if we are in a stopped state. */ if (STOPLWP(p, lp)) { lwkt_gettoken(&p->p_token); tstop(); lwkt_reltoken(&p->p_token); goto recheck; } while (dump_stop_usertds) { tsleep(&dump_stop_usertds, 0, "dumpstp", 0); } /* * Post any pending upcalls. If running a virtual kernel be sure * to restore the virtual kernel's vmspace before posting the upcall. */ if (p->p_flags & (P_SIGVTALRM | P_SIGPROF)) { lwkt_gettoken(&p->p_token); if (p->p_flags & P_SIGVTALRM) { p->p_flags &= ~P_SIGVTALRM; ksignal(p, SIGVTALRM); } if (p->p_flags & P_SIGPROF) { p->p_flags &= ~P_SIGPROF; ksignal(p, SIGPROF); } lwkt_reltoken(&p->p_token); goto recheck; } /* * Post any pending signals. If running a virtual kernel be sure * to restore the virtual kernel's vmspace before posting the signal. * * WARNING! postsig() can exit and not return. */ if ((sig = CURSIG_LCK_TRACE(lp, &ptok)) != 0) { postsig(sig, ptok); goto recheck; } /* * block here if we are swapped out, but still process signals * (such as SIGKILL). proc0 (the swapin scheduler) is already * aware of our situation, we do not have to wake it up. */ if (p->p_flags & P_SWAPPEDOUT) { lwkt_gettoken(&p->p_token); p->p_flags |= P_SWAPWAIT; swapin_request(); if (p->p_flags & P_SWAPWAIT) tsleep(p, PCATCH, "SWOUT", 0); p->p_flags &= ~P_SWAPWAIT; lwkt_reltoken(&p->p_token); goto recheck; } /* * In a multi-threaded program it is possible for a thread to change * signal state during a system call which temporarily changes the * signal mask. In this case postsig() might not be run and we * have to restore the mask ourselves. */ if (lp->lwp_flags & LWP_OLDMASK) { lp->lwp_flags &= ~LWP_OLDMASK; lp->lwp_sigmask = lp->lwp_oldsigmask; goto recheck; } }
/* * do an ioctl operation on a pfsnode (vp). * (vp) is not locked on entry or exit. */ static int procfs_ioctl(struct vop_ioctl_args *ap) { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *procp; struct proc *p; int error; int signo; struct procfs_status *psp; unsigned char flags; procp = pfs_pfind(pfs->pfs_pid); if (procp == NULL) return ENOTTY; p = curproc; if (p == NULL) { error = EINVAL; goto done; } /* Can't trace a process that's currently exec'ing. */ if ((procp->p_flags & P_INEXEC) != 0) { error = EAGAIN; goto done; } if (!CHECKIO(p, procp) || p_trespass(ap->a_cred, procp->p_ucred)) { error = EPERM; goto done; } switch (ap->a_command) { case PIOCBIS: spin_lock(&procp->p_spin); procp->p_stops |= *(unsigned int*)ap->a_data; spin_unlock(&procp->p_spin); break; case PIOCBIC: spin_lock(&procp->p_spin); procp->p_stops &= ~*(unsigned int*)ap->a_data; spin_unlock(&procp->p_spin); break; case PIOCSFL: /* * NFLAGS is "non-suser_xxx flags" -- currently, only * PFS_ISUGID ("ignore set u/g id"); */ #define NFLAGS (PF_ISUGID) flags = (unsigned char)*(unsigned int*)ap->a_data; if (flags & NFLAGS && (error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0))) goto done; procp->p_pfsflags = flags; break; case PIOCGFL: *(unsigned int*)ap->a_data = (unsigned int)procp->p_pfsflags; break; case PIOCSTATUS: /* * NOTE: syscall entry deals with stopevents and may run without * the MP lock. */ psp = (struct procfs_status *)ap->a_data; psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; spin_lock(&procp->p_spin); if (procp->p_step) { psp->state = 0; psp->why = procp->p_stype; psp->val = procp->p_xstat; spin_unlock(&procp->p_spin); } else { psp->state = 1; spin_unlock(&procp->p_spin); psp->why = 0; /* Not defined values */ psp->val = 0; /* Not defined values */ } break; case PIOCWAIT: /* * NOTE: syscall entry deals with stopevents and may run without * the MP lock. */ psp = (struct procfs_status *)ap->a_data; spin_lock(&procp->p_spin); while (procp->p_step == 0) { tsleep_interlock(&procp->p_stype, PCATCH); spin_unlock(&procp->p_spin); if (procp->p_stops == 0) { error = EINVAL; goto done; } if (procp->p_flags & P_POSTEXIT) { error = EINVAL; goto done; } if (procp->p_flags & P_INEXEC) { error = EAGAIN; goto done; } error = tsleep(&procp->p_stype, PCATCH | PINTERLOCKED, "piocwait", 0); if (error) goto done; spin_lock(&procp->p_spin); } spin_unlock(&procp->p_spin); psp->state = 1; /* It stopped */ psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; psp->why = procp->p_stype; /* why it stopped */ psp->val = procp->p_xstat; /* any extra info */ break; case PIOCCONT: /* Restart a proc */ /* * NOTE: syscall entry deals with stopevents and may run without * the MP lock. However, the caller is presumably interlocked * by having waited. */ if (procp->p_step == 0) { error = EINVAL; /* Can only start a stopped process */ goto done; } if ((signo = *(int*)ap->a_data) != 0) { if (signo >= NSIG || signo <= 0) { error = EINVAL; goto done; } ksignal(procp, signo); } procp->p_step = 0; wakeup(&procp->p_step); break; default: error = ENOTTY; goto done; } error = 0; done: pfs_pdone(procp); return 0; }
/* * Handle signals, upcalls, profiling, and other AST's and/or tasks that * must be completed before we can return to or try to return to userland. * * Note that td_sticks is a 64 bit quantity, but there's no point doing 64 * arithmatic on the delta calculation so the absolute tick values are * truncated to an integer. */ static void userret(struct lwp *lp, struct trapframe *frame, int sticks) { struct proc *p = lp->lwp_proc; void (*hook)(void); int sig; if (p->p_userret != NULL) { hook = p->p_userret; p->p_userret = NULL; (*hook)(); } /* * Charge system time if profiling. Note: times are in microseconds. * This may do a copyout and block, so do it first even though it * means some system time will be charged as user time. */ if (p->p_flags & P_PROFIL) { addupc_task(p, frame->tf_eip, (u_int)((int)lp->lwp_thread->td_sticks - sticks)); } recheck: /* * If the jungle wants us dead, so be it. */ if (lp->lwp_mpflags & LWP_MP_WEXIT) { lwkt_gettoken(&p->p_token); lwp_exit(0); lwkt_reltoken(&p->p_token); /* NOT REACHED */ } /* * Block here if we are in a stopped state. */ if (p->p_stat == SSTOP || dump_stop_usertds) { lwkt_gettoken(&p->p_token); tstop(); lwkt_reltoken(&p->p_token); goto recheck; } /* * Post any pending upcalls. If running a virtual kernel be sure * to restore the virtual kernel's vmspace before posting the upcall. */ if (p->p_flags & (P_SIGVTALRM | P_SIGPROF | P_UPCALLPEND)) { lwkt_gettoken(&p->p_token); if (p->p_flags & P_SIGVTALRM) { p->p_flags &= ~P_SIGVTALRM; ksignal(p, SIGVTALRM); } if (p->p_flags & P_SIGPROF) { p->p_flags &= ~P_SIGPROF; ksignal(p, SIGPROF); } if (p->p_flags & P_UPCALLPEND) { p->p_flags &= ~P_UPCALLPEND; postupcall(lp); } lwkt_reltoken(&p->p_token); goto recheck; } /* * Post any pending signals. If running a virtual kernel be sure * to restore the virtual kernel's vmspace before posting the signal. * * WARNING! postsig() can exit and not return. */ if ((sig = CURSIG_TRACE(lp)) != 0) { lwkt_gettoken(&p->p_token); postsig(sig); lwkt_reltoken(&p->p_token); goto recheck; } /* * block here if we are swapped out, but still process signals * (such as SIGKILL). proc0 (the swapin scheduler) is already * aware of our situation, we do not have to wake it up. */ if (p->p_flags & P_SWAPPEDOUT) { lwkt_gettoken(&p->p_token); get_mplock(); p->p_flags |= P_SWAPWAIT; swapin_request(); if (p->p_flags & P_SWAPWAIT) tsleep(p, PCATCH, "SWOUT", 0); p->p_flags &= ~P_SWAPWAIT; rel_mplock(); lwkt_reltoken(&p->p_token); goto recheck; } /* * In a multi-threaded program it is possible for a thread to change * signal state during a system call which temporarily changes the * signal mask. In this case postsig() might not be run and we * have to restore the mask ourselves. */ if (lp->lwp_flags & LWP_OLDMASK) { lp->lwp_flags &= ~LWP_OLDMASK; lp->lwp_sigmask = lp->lwp_oldsigmask; goto recheck; } }