summaryrefslogtreecommitdiffstats
path: root/sys/kern/uipc_usrreq.c
diff options
context:
space:
mode:
authoralfred <alfred@FreeBSD.org>2002-01-13 11:58:06 +0000
committeralfred <alfred@FreeBSD.org>2002-01-13 11:58:06 +0000
commit844237b3960bfbf49070d6371a84f67f9e3366f6 (patch)
tree598e20df363e602313c7ad93de8f8c4b4240d61d /sys/kern/uipc_usrreq.c
parent8cd61193307ff459ae72eb7aa6a734eb5e3b427e (diff)
downloadFreeBSD-src-844237b3960bfbf49070d6371a84f67f9e3366f6.zip
FreeBSD-src-844237b3960bfbf49070d6371a84f67f9e3366f6.tar.gz
SMP Lock struct file, filedesc and the global file list.
Seigo Tanimura (tanimura) posted the initial delta. I've polished it quite a bit reducing the need for locking and adapting it for KSE. Locks: 1 mutex in each filedesc protects all the fields. protects "struct file" initialization, while a struct file is being changed from &badfileops -> &pipeops or something the filedesc should be locked. 1 mutex in each struct file protects the refcount fields. doesn't protect anything else. the flags used for garbage collection have been moved to f_gcflag which was the FILLER short, this doesn't need locking because the garbage collection is a single threaded container. could likely be made to use a pool mutex. 1 sx lock for the global filelist. struct file * fhold(struct file *fp); /* increments reference count on a file */ struct file * fhold_locked(struct file *fp); /* like fhold but expects file to locked */ struct file * ffind_hold(struct thread *, int fd); /* finds the struct file in thread, adds one reference and returns it unlocked */ struct file * ffind_lock(struct thread *, int fd); /* ffind_hold, but returns file locked */ I still have to smp-safe the fget cruft, I'll get to that asap.
Diffstat (limited to 'sys/kern/uipc_usrreq.c')
-rw-r--r--sys/kern/uipc_usrreq.c70
1 files changed, 55 insertions, 15 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index a7ffcff..546124d 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -57,6 +57,7 @@
#include <sys/unpcb.h>
#include <sys/vnode.h>
#include <sys/jail.h>
+#include <sys/sx.h>
#include <vm/vm_zone.h>
@@ -535,7 +536,9 @@ unp_attach(so)
unp_count++;
LIST_INIT(&unp->unp_refs);
unp->unp_socket = so;
+ FILEDESC_LOCK(curproc->p_fd);
unp->unp_rvnode = curthread->td_proc->p_fd->fd_rdir;
+ FILEDESC_UNLOCK(curproc->p_fd);
LIST_INSERT_HEAD(so->so_type == SOCK_DGRAM ? &unp_dhead
: &unp_shead, unp, unp_link);
so->so_pcb = (caddr_t)unp;
@@ -628,7 +631,9 @@ restart:
}
VATTR_NULL(&vattr);
vattr.va_type = VSOCK;
+ FILEDESC_LOCK(td->td_proc->p_fd);
vattr.va_mode = (ACCESSPERMS & ~td->td_proc->p_fd->fd_cmask);
+ FILEDESC_UNLOCK(td->td_proc->p_fd);
VOP_LEASE(nd.ni_dvp, td, td->td_proc->p_ucred, LEASE_WRITE);
error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
NDFREE(&nd, NDF_ONLY_PNBUF);
@@ -1006,8 +1011,10 @@ unp_externalize(control, controlp)
unp_freerights(rp, newfds);
goto next;
}
+ FILEDESC_LOCK(td->td_proc->p_fd);
/* if the new FD's will not fit free them. */
if (!fdavail(td, newfds)) {
+ FILEDESC_UNLOCK(td->td_proc->p_fd);
error = EMSGSIZE;
unp_freerights(rp, newfds);
goto next;
@@ -1022,6 +1029,7 @@ unp_externalize(control, controlp)
*controlp = sbcreatecontrol(NULL, newlen,
SCM_RIGHTS, SOL_SOCKET);
if (*controlp == NULL) {
+ FILEDESC_UNLOCK(td->td_proc->p_fd);
error = E2BIG;
unp_freerights(rp, newfds);
goto next;
@@ -1034,10 +1042,13 @@ unp_externalize(control, controlp)
panic("unp_externalize fdalloc failed");
fp = *rp++;
td->td_proc->p_fd->fd_ofiles[f] = fp;
+ FILE_LOCK(fp);
fp->f_msgcount--;
+ FILE_UNLOCK(fp);
unp_rights--;
*fdp++ = f;
}
+ FILEDESC_UNLOCK(td->td_proc->p_fd);
} else { /* We can just copy anything else across */
if (error || controlp == NULL)
goto next;
@@ -1064,6 +1075,7 @@ next:
cm = NULL;
}
}
+ FILEDESC_UNLOCK(td->td_proc->p_fd);
m_freem(control);
@@ -1148,10 +1160,12 @@ unp_internalize(controlp, td)
* If not, reject the entire operation.
*/
fdp = data;
+ FILEDESC_LOCK(fdescp);
for (i = 0; i < oldfds; i++) {
fd = *fdp++;
if ((unsigned)fd >= fdescp->fd_nfiles ||
fdescp->fd_ofiles[fd] == NULL) {
+ FILEDESC_UNLOCK(fdescp);
error = EBADF;
goto out;
}
@@ -1164,6 +1178,7 @@ unp_internalize(controlp, td)
*controlp = sbcreatecontrol(NULL, newlen,
SCM_RIGHTS, SOL_SOCKET);
if (*controlp == NULL) {
+ FILEDESC_UNLOCK(fdescp);
error = E2BIG;
goto out;
}
@@ -1174,10 +1189,13 @@ unp_internalize(controlp, td)
for (i = 0; i < oldfds; i++) {
fp = fdescp->fd_ofiles[*fdp++];
*rp++ = fp;
+ FILE_LOCK(fp);
fp->f_count++;
fp->f_msgcount++;
+ FILE_UNLOCK(fp);
unp_rights++;
}
+ FILEDESC_UNLOCK(fdescp);
break;
case SCM_TIMESTAMP:
@@ -1233,42 +1251,50 @@ unp_gc()
* before going through all this, set all FDs to
* be NOT defered and NOT externally accessible
*/
+ sx_slock(&filelist_lock);
LIST_FOREACH(fp, &filehead, f_list)
- fp->f_flag &= ~(FMARK|FDEFER);
+ fp->f_gcflag &= ~(FMARK|FDEFER);
do {
LIST_FOREACH(fp, &filehead, f_list) {
+ FILE_LOCK(fp);
/*
* If the file is not open, skip it
*/
- if (fp->f_count == 0)
+ if (fp->f_count == 0) {
+ FILE_UNLOCK(fp);
continue;
+ }
/*
* If we already marked it as 'defer' in a
* previous pass, then try process it this time
* and un-mark it
*/
- if (fp->f_flag & FDEFER) {
- fp->f_flag &= ~FDEFER;
+ if (fp->f_gcflag & FDEFER) {
+ fp->f_gcflag &= ~FDEFER;
unp_defer--;
} else {
/*
* if it's not defered, then check if it's
* already marked.. if so skip it
*/
- if (fp->f_flag & FMARK)
+ if (fp->f_gcflag & FMARK) {
+ FILE_UNLOCK(fp);
continue;
+ }
/*
* If all references are from messages
* in transit, then skip it. it's not
* externally accessible.
*/
- if (fp->f_count == fp->f_msgcount)
+ if (fp->f_count == fp->f_msgcount) {
+ FILE_UNLOCK(fp);
continue;
+ }
/*
* If it got this far then it must be
* externally accessible.
*/
- fp->f_flag |= FMARK;
+ fp->f_gcflag |= FMARK;
}
/*
* either it was defered, or it is externally
@@ -1276,8 +1302,11 @@ unp_gc()
* Now check if it is possibly one of OUR sockets.
*/
if (fp->f_type != DTYPE_SOCKET ||
- (so = (struct socket *)fp->f_data) == 0)
+ (so = (struct socket *)fp->f_data) == 0) {
+ FILE_UNLOCK(fp);
continue;
+ }
+ FILE_UNLOCK(fp);
if (so->so_proto->pr_domain != &localdomain ||
(so->so_proto->pr_flags&PR_RIGHTS) == 0)
continue;
@@ -1307,6 +1336,7 @@ unp_gc()
unp_scan(so->so_rcv.sb_mb, unp_mark);
}
} while (unp_defer);
+ sx_sunlock(&filelist_lock);
/*
* We grab an extra reference to each of the file table entries
* that are not otherwise accessible and then free the rights
@@ -1347,33 +1377,43 @@ unp_gc()
* 91/09/19, bsy@cs.cmu.edu
*/
extra_ref = malloc(nfiles * sizeof(struct file *), M_FILE, M_WAITOK);
+ sx_slock(&filelist_lock);
for (nunref = 0, fp = LIST_FIRST(&filehead), fpp = extra_ref; fp != 0;
fp = nextfp) {
nextfp = LIST_NEXT(fp, f_list);
+ FILE_LOCK(fp);
/*
* If it's not open, skip it
*/
- if (fp->f_count == 0)
+ if (fp->f_count == 0) {
+ FILE_UNLOCK(fp);
continue;
+ }
/*
* If all refs are from msgs, and it's not marked accessible
* then it must be referenced from some unreachable cycle
* of (shut-down) FDs, so include it in our
* list of FDs to remove
*/
- if (fp->f_count == fp->f_msgcount && !(fp->f_flag & FMARK)) {
+ if (fp->f_count == fp->f_msgcount && !(fp->f_gcflag & FMARK)) {
*fpp++ = fp;
nunref++;
fp->f_count++;
}
+ FILE_UNLOCK(fp);
}
+ sx_sunlock(&filelist_lock);
/*
* for each FD on our hit list, do the following two things
*/
for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp) {
struct file *tfp = *fpp;
- if (tfp->f_type == DTYPE_SOCKET && tfp->f_data != NULL)
+ FILE_LOCK(tfp);
+ if (tfp->f_type == DTYPE_SOCKET && tfp->f_data != NULL) {
+ FILE_UNLOCK(tfp);
sorflush((struct socket *)(tfp->f_data));
+ } else
+ FILE_UNLOCK(tfp);
}
for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp)
closef(*fpp, (struct thread *) NULL);
@@ -1460,19 +1500,19 @@ static void
unp_mark(fp)
struct file *fp;
{
-
- if (fp->f_flag & FMARK)
+ if (fp->f_gcflag & FMARK)
return;
unp_defer++;
- fp->f_flag |= (FMARK|FDEFER);
+ fp->f_gcflag |= (FMARK|FDEFER);
}
static void
unp_discard(fp)
struct file *fp;
{
-
+ FILE_LOCK(fp);
fp->f_msgcount--;
unp_rights--;
+ FILE_UNLOCK(fp);
(void) closef(fp, (struct thread *)NULL);
}
OpenPOWER on IntegriCloud