aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGleb Smirnoff <glebius@FreeBSD.org>2014-11-11 20:32:46 +0000
committerGleb Smirnoff <glebius@FreeBSD.org>2014-11-11 20:32:46 +0000
commit0e87b36eaac0db20b8cbb28ed1950c7fa121c2a5 (patch)
tree967169abc54d43fd8346947a20fafbb002a8a8bf
parent7bfc98355a6de3a6b634e38e6ddd5c7303324e1e (diff)
Notes
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c46
-rw-r--r--sys/kern/kern_descrip.c4
-rw-r--r--sys/kern/uipc_syscalls.c730
-rw-r--r--sys/sys/file.h10
-rw-r--r--sys/sys/sf_base.h37
-rw-r--r--sys/sys/sf_sync.h64
-rw-r--r--sys/sys/socket.h16
7 files changed, 69 insertions, 838 deletions
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index 5ea062ea3686..b2ec8b357d84 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -83,10 +83,6 @@ __FBSDID("$FreeBSD$");
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/shm.h>
-#include <sys/condvar.h>
-#include <sys/sf_buf.h>
-#include <sys/sf_sync.h>
-#include <sys/sf_base.h>
#ifdef INET
#include <netinet/in.h>
@@ -1567,26 +1563,16 @@ struct sf_hdtr32 {
int trl_cnt;
};
-struct sf_hdtr_kq32 {
- int kq_fd;
- uint32_t kq_flags;
- uint32_t kq_udata; /* 32-bit void ptr */
- uint32_t kq_ident; /* 32-bit uintptr_t */
-};
-
static int
freebsd32_do_sendfile(struct thread *td,
struct freebsd32_sendfile_args *uap, int compat)
{
struct sf_hdtr32 hdtr32;
struct sf_hdtr hdtr;
- struct sf_hdtr_kq32 hdtr_kq32;
- struct sf_hdtr_kq hdtr_kq;
struct uio *hdr_uio, *trl_uio;
struct iovec32 *iov32;
- off_t offset;
+ off_t offset, sbytes;
int error;
- off_t sbytes;
offset = PAIR32TO64(off_t, uap->offset);
if (offset < 0)
@@ -1617,31 +1603,17 @@ freebsd32_do_sendfile(struct thread *td,
if (error)
goto out;
}
-
- /*
- * If SF_KQUEUE is set, then we need to also copy in
- * the kqueue data after the normal hdtr set and set do_kqueue=1.
- */
- if (uap->flags & SF_KQUEUE) {
- error = copyin(((char *) uap->hdtr) + sizeof(hdtr32),
- &hdtr_kq32,
- sizeof(hdtr_kq32));
- if (error != 0)
- goto out;
-
- /* 32->64 bit fields */
- CP(hdtr_kq32, hdtr_kq, kq_fd);
- CP(hdtr_kq32, hdtr_kq, kq_flags);
- PTRIN_CP(hdtr_kq32, hdtr_kq, kq_udata);
- CP(hdtr_kq32, hdtr_kq, kq_ident);
- }
}
+ AUDIT_ARG_FD(uap->fd);
+
+ if ((error = fget_read(td, uap->fd,
+ cap_rights_init(&rights, CAP_PREAD), &fp)) != 0)
+ goto out;
- /* Call sendfile */
- /* XXX stack depth! */
- error = _do_sendfile(td, uap->fd, uap->s, uap->flags, compat,
- offset, uap->nbytes, &sbytes, hdr_uio, trl_uio, &hdtr_kq);
+ error = fo_sendfile(fp, uap->s, hdr_uio, trl_uio, offset,
+ uap->nbytes, &sbytes, uap->flags, compat ? SFK_COMPAT : 0, td);
+ fdrop(fp, td);
if (uap->sbytes != NULL)
copyout(&sbytes, uap->sbytes, sizeof(off_t));
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 32c837c57c1a..e955b8753fdb 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -3684,7 +3684,7 @@ badfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
static int
badfo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags,
- int kflags, struct sendfile_sync *sfs, struct thread *td)
+ int kflags, struct thread *td)
{
return (EBADF);
@@ -3770,7 +3770,7 @@ invfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
int
invfo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags,
- int kflags, struct sendfile_sync *sfs, struct thread *td)
+ int kflags, struct thread *td)
{
return (EINVAL);
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
index 6d423ba5106e..b53883deff76 100644
--- a/sys/kern/uipc_syscalls.c
+++ b/sys/kern/uipc_syscalls.c
@@ -63,8 +63,6 @@ __FBSDID("$FreeBSD$");
#include <sys/protosw.h>
#include <sys/rwlock.h>
#include <sys/sf_buf.h>
-#include <sys/sf_sync.h>
-#include <sys/sf_base.h>
#include <sys/sysent.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
@@ -115,10 +113,6 @@ static int getpeername1(struct thread *td, struct getpeername_args *uap,
counter_u64_t sfstat[sizeof(struct sfstat) / sizeof(uint64_t)];
-static int filt_sfsync_attach(struct knote *kn);
-static void filt_sfsync_detach(struct knote *kn);
-static int filt_sfsync(struct knote *kn, long hint);
-
/*
* sendfile(2)-related variables and associated sysctls
*/
@@ -128,28 +122,6 @@ static int sfreadahead = 1;
SYSCTL_INT(_kern_ipc_sendfile, OID_AUTO, readahead, CTLFLAG_RW,
&sfreadahead, 0, "Number of sendfile(2) read-ahead MAXBSIZE blocks");
-#ifdef SFSYNC_DEBUG
-static int sf_sync_debug = 0;
-SYSCTL_INT(_debug, OID_AUTO, sf_sync_debug, CTLFLAG_RW,
- &sf_sync_debug, 0, "Output debugging during sf_sync lifecycle");
-#define SFSYNC_DPRINTF(s, ...) \
- do { \
- if (sf_sync_debug) \
- printf((s), ##__VA_ARGS__); \
- } while (0)
-#else
-#define SFSYNC_DPRINTF(c, ...)
-#endif
-
-static uma_zone_t zone_sfsync;
-
-static struct filterops sendfile_filtops = {
- .f_isfd = 0,
- .f_attach = filt_sfsync_attach,
- .f_detach = filt_sfsync_detach,
- .f_event = filt_sfsync,
-};
-
static void
sfstat_init(const void *unused)
{
@@ -159,19 +131,6 @@ sfstat_init(const void *unused)
}
SYSINIT(sfstat, SI_SUB_MBUF, SI_ORDER_FIRST, sfstat_init, NULL);
-static void
-sf_sync_init(const void *unused)
-{
-
- zone_sfsync = uma_zcreate("sendfile_sync", sizeof(struct sendfile_sync),
- NULL, NULL,
- NULL, NULL,
- UMA_ALIGN_CACHE,
- 0);
- kqueue_add_filteropts(EVFILT_SENDFILE, &sendfile_filtops);
-}
-SYSINIT(sf_sync, SI_SUB_MBUF, SI_ORDER_FIRST, sf_sync_init, NULL);
-
static int
sfstat_sysctl(SYSCTL_HANDLER_ARGS)
{
@@ -1864,116 +1823,11 @@ getsockaddr(namp, uaddr, len)
return (error);
}
-static int
-filt_sfsync_attach(struct knote *kn)
-{
- struct sendfile_sync *sfs = (struct sendfile_sync *) kn->kn_sdata;
- struct knlist *knl = &sfs->klist;
-
- SFSYNC_DPRINTF("%s: kn=%p, sfs=%p\n", __func__, kn, sfs);
-
- /*
- * Validate that we actually received this via the kernel API.
- */
- if ((kn->kn_flags & EV_FLAG1) == 0)
- return (EPERM);
-
- kn->kn_ptr.p_v = sfs;
- kn->kn_flags &= ~EV_FLAG1;
-
- knl->kl_lock(knl->kl_lockarg);
- /*
- * If we're in the "freeing" state,
- * don't allow the add. That way we don't
- * end up racing with some other thread that
- * is trying to finish some setup.
- */
- if (sfs->state == SF_STATE_FREEING) {
- knl->kl_unlock(knl->kl_lockarg);
- return (EINVAL);
- }
- knlist_add(&sfs->klist, kn, 1);
- knl->kl_unlock(knl->kl_lockarg);
-
- return (0);
-}
-
-/*
- * Called when a knote is being detached.
- */
-static void
-filt_sfsync_detach(struct knote *kn)
-{
- struct knlist *knl;
- struct sendfile_sync *sfs;
- int do_free = 0;
-
- sfs = kn->kn_ptr.p_v;
- knl = &sfs->klist;
-
- SFSYNC_DPRINTF("%s: kn=%p, sfs=%p\n", __func__, kn, sfs);
-
- knl->kl_lock(knl->kl_lockarg);
- if (!knlist_empty(knl))
- knlist_remove(knl, kn, 1);
-
- /*
- * If the list is empty _AND_ the refcount is 0
- * _AND_ we've finished the setup phase and now
- * we're in the running phase, we can free the
- * underlying sendfile_sync.
- *
- * But we shouldn't do it before finishing the
- * underlying divorce from the knote.
- *
- * So, we have the sfsync lock held; transition
- * it to "freeing", then unlock, then free
- * normally.
- */
- if (knlist_empty(knl)) {
- if (sfs->state == SF_STATE_COMPLETED && sfs->count == 0) {
- SFSYNC_DPRINTF("%s: (%llu) sfs=%p; completed, "
- "count==0, empty list: time to free!\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs);
- sf_sync_set_state(sfs, SF_STATE_FREEING, 1);
- do_free = 1;
- }
- }
- knl->kl_unlock(knl->kl_lockarg);
-
- /*
- * Only call free if we're the one who has transitioned things
- * to free. Otherwise we could race with another thread that
- * is currently tearing things down.
- */
- if (do_free == 1) {
- SFSYNC_DPRINTF("%s: (%llu) sfs=%p, %s:%d\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs,
- __FILE__,
- __LINE__);
- sf_sync_free(sfs);
- }
-}
-
-static int
-filt_sfsync(struct knote *kn, long hint)
-{
- struct sendfile_sync *sfs = (struct sendfile_sync *) kn->kn_ptr.p_v;
- int ret;
-
- SFSYNC_DPRINTF("%s: kn=%p, sfs=%p\n", __func__, kn, sfs);
-
- /*
- * XXX add a lock assertion here!
- */
- ret = (sfs->count == 0 && sfs->state == SF_STATE_COMPLETED);
-
- return (ret);
-}
+struct sendfile_sync {
+ struct mtx mtx;
+ struct cv cv;
+ unsigned count;
+};
/*
* Add more references to a vm_page + sf_buf + sendfile_sync.
@@ -2022,344 +1876,13 @@ sf_ext_free(void *arg1, void *arg2)
vm_page_free(pg);
vm_page_unlock(pg);
- if (sfs != NULL)
- sf_sync_deref(sfs);
-}
-
-/*
- * Called to remove a reference to a sf_sync object.
- *
- * This is generally done during the mbuf free path to signify
- * that one of the mbufs in the transaction has been completed.
- *
- * If we're doing SF_SYNC and the refcount is zero then we'll wake
- * up any waiters.
- *
- * IF we're doing SF_KQUEUE and the refcount is zero then we'll
- * fire off the knote.
- */
-void
-sf_sync_deref(struct sendfile_sync *sfs)
-{
- int do_free = 0;
-
- if (sfs == NULL)
- return;
-
- mtx_lock(&sfs->mtx);
- KASSERT(sfs->count> 0, ("Sendfile sync botchup count == 0"));
- sfs->count --;
-
- /*
- * Only fire off the wakeup / kqueue notification if
- * we are in the running state.
- */
- if (sfs->count == 0 && sfs->state == SF_STATE_COMPLETED) {
- if (sfs->flags & SF_SYNC)
+ if (sfs != NULL) {
+ mtx_lock(&sfs->mtx);
+ KASSERT(sfs->count > 0, ("Sendfile sync botchup count == 0"));
+ if (--sfs->count == 0)
cv_signal(&sfs->cv);
-
- if (sfs->flags & SF_KQUEUE) {
- SFSYNC_DPRINTF("%s: (%llu) sfs=%p: knote!\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs);
- KNOTE_LOCKED(&sfs->klist, 1);
- }
-
- /*
- * If we're not waiting around for a sync,
- * check if the knote list is empty.
- * If it is, we transition to free.
- *
- * XXX I think it's about time I added some state
- * or flag that says whether we're supposed to be
- * waiting around until we've done a signal.
- *
- * XXX Ie, the reason that I don't free it here
- * is because the caller will free the last reference,
- * not us. That should be codified in some flag
- * that indicates "self-free" rather than checking
- * for SF_SYNC all the time.
- */
- if ((sfs->flags & SF_SYNC) == 0 && knlist_empty(&sfs->klist)) {
- SFSYNC_DPRINTF("%s: (%llu) sfs=%p; completed, "
- "count==0, empty list: time to free!\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs);
- sf_sync_set_state(sfs, SF_STATE_FREEING, 1);
- do_free = 1;
- }
-
- }
- mtx_unlock(&sfs->mtx);
-
- /*
- * Attempt to do a free here.
- *
- * We do this outside of the lock because it may destroy the
- * lock in question as it frees things. We can optimise this
- * later.
- *
- * XXX yes, we should make it a requirement to hold the
- * lock across sf_sync_free().
- */
- if (do_free == 1) {
- SFSYNC_DPRINTF("%s: (%llu) sfs=%p\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs);
- sf_sync_free(sfs);
- }
-}
-
-/*
- * Allocate a sendfile_sync state structure.
- *
- * For now this only knows about the "sleep" sync, but later it will
- * grow various other personalities.
- */
-struct sendfile_sync *
-sf_sync_alloc(uint32_t flags)
-{
- struct sendfile_sync *sfs;
-
- sfs = uma_zalloc(zone_sfsync, M_WAITOK | M_ZERO);
- mtx_init(&sfs->mtx, "sendfile", NULL, MTX_DEF);
- cv_init(&sfs->cv, "sendfile");
- sfs->flags = flags;
- sfs->state = SF_STATE_SETUP;
- knlist_init_mtx(&sfs->klist, &sfs->mtx);
-
- SFSYNC_DPRINTF("%s: sfs=%p, flags=0x%08x\n", __func__, sfs, sfs->flags);
-
- return (sfs);
-}
-
-/*
- * Take a reference to a sfsync instance.
- *
- * This has to map 1:1 to free calls coming in via sf_ext_free(),
- * so typically this will be referenced once for each mbuf allocated.
- */
-void
-sf_sync_ref(struct sendfile_sync *sfs)
-{
-
- if (sfs == NULL)
- return;
-
- mtx_lock(&sfs->mtx);
- sfs->count++;
- mtx_unlock(&sfs->mtx);
-}
-
-void
-sf_sync_syscall_wait(struct sendfile_sync *sfs)
-{
-
- if (sfs == NULL)
- return;
-
- KASSERT(mtx_owned(&sfs->mtx), ("%s: sfs=%p: not locked but should be!",
- __func__,
- sfs));
-
- /*
- * If we're not requested to wait during the syscall,
- * don't bother waiting.
- */
- if ((sfs->flags & SF_SYNC) == 0)
- goto out;
-
- /*
- * This is a bit suboptimal and confusing, so bear with me.
- *
- * Ideally sf_sync_syscall_wait() will wait until
- * all pending mbuf transmit operations are done.
- * This means that when sendfile becomes async, it'll
- * run in the background and will transition from
- * RUNNING to COMPLETED when it's finished acquiring
- * new things to send. Then, when the mbufs finish
- * sending, COMPLETED + sfs->count == 0 is enough to
- * know that no further work is being done.
- *
- * So, we will sleep on both RUNNING and COMPLETED.
- * It's up to the (in progress) async sendfile loop
- * to transition the sf_sync from RUNNING to
- * COMPLETED so the wakeup above will actually
- * do the cv_signal() call.
- */
- if (sfs->state != SF_STATE_COMPLETED && sfs->state != SF_STATE_RUNNING)
- goto out;
-
- if (sfs->count != 0)
- cv_wait(&sfs->cv, &sfs->mtx);
- KASSERT(sfs->count == 0, ("sendfile sync still busy"));
-
-out:
- return;
-}
-
-/*
- * Free an sf_sync if it's appropriate to.
- */
-void
-sf_sync_free(struct sendfile_sync *sfs)
-{
-
- if (sfs == NULL)
- return;
-
- SFSYNC_DPRINTF("%s: (%lld) sfs=%p; called; state=%d, flags=0x%08x "
- "count=%d\n",
- __func__,
- (long long) curthread->td_tid,
- sfs,
- sfs->state,
- sfs->flags,
- sfs->count);
-
- mtx_lock(&sfs->mtx);
-
- /*
- * We keep the sf_sync around if the state is active,
- * we are doing kqueue notification and we have active
- * knotes.
- *
- * If the caller wants to free us right this second it
- * should transition this to the freeing state.
- *
- * So, complain loudly if they break this rule.
- */
- if (sfs->state != SF_STATE_FREEING) {
- printf("%s: (%llu) sfs=%p; not freeing; let's wait!\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs);
mtx_unlock(&sfs->mtx);
- return;
- }
-
- KASSERT(sfs->count == 0, ("sendfile sync still busy"));
- cv_destroy(&sfs->cv);
- /*
- * This doesn't call knlist_detach() on each knote; it just frees
- * the entire list.
- */
- knlist_delete(&sfs->klist, curthread, 1);
- mtx_destroy(&sfs->mtx);
- SFSYNC_DPRINTF("%s: (%llu) sfs=%p; freeing\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs);
- uma_zfree(zone_sfsync, sfs);
-}
-
-/*
- * Setup a sf_sync to post a kqueue notification when things are complete.
- */
-int
-sf_sync_kqueue_setup(struct sendfile_sync *sfs, struct sf_hdtr_kq *sfkq)
-{
- struct kevent kev;
- int error;
-
- sfs->flags |= SF_KQUEUE;
-
- /* Check the flags are valid */
- if ((sfkq->kq_flags & ~(EV_CLEAR | EV_DISPATCH | EV_ONESHOT)) != 0)
- return (EINVAL);
-
- SFSYNC_DPRINTF("%s: sfs=%p: kqfd=%d, flags=0x%08x, ident=%p, udata=%p\n",
- __func__,
- sfs,
- sfkq->kq_fd,
- sfkq->kq_flags,
- (void *) sfkq->kq_ident,
- (void *) sfkq->kq_udata);
-
- /* Setup and register a knote on the given kqfd. */
- kev.ident = (uintptr_t) sfkq->kq_ident;
- kev.filter = EVFILT_SENDFILE;
- kev.flags = EV_ADD | EV_ENABLE | EV_FLAG1 | sfkq->kq_flags;
- kev.data = (intptr_t) sfs;
- kev.udata = sfkq->kq_udata;
-
- error = kqfd_register(sfkq->kq_fd, &kev, curthread, 1);
- if (error != 0) {
- SFSYNC_DPRINTF("%s: returned %d\n", __func__, error);
}
- return (error);
-}
-
-void
-sf_sync_set_state(struct sendfile_sync *sfs, sendfile_sync_state_t state,
- int islocked)
-{
- sendfile_sync_state_t old_state;
-
- if (! islocked)
- mtx_lock(&sfs->mtx);
-
- /*
- * Update our current state.
- */
- old_state = sfs->state;
- sfs->state = state;
- SFSYNC_DPRINTF("%s: (%llu) sfs=%p; going from %d to %d\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs,
- old_state,
- state);
-
- /*
- * If we're transitioning from RUNNING to COMPLETED and the count is
- * zero, then post the knote. The caller may have completed the
- * send before we updated the state to COMPLETED and we need to make
- * sure this is communicated.
- */
- if (old_state == SF_STATE_RUNNING
- && state == SF_STATE_COMPLETED
- && sfs->count == 0
- && sfs->flags & SF_KQUEUE) {
- SFSYNC_DPRINTF("%s: (%llu) sfs=%p: triggering knote!\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs);
- KNOTE_LOCKED(&sfs->klist, 1);
- }
-
- if (! islocked)
- mtx_unlock(&sfs->mtx);
-}
-
-/*
- * Set the retval/errno for the given transaction.
- *
- * This will eventually/ideally be used when the KNOTE is fired off
- * to signify the completion of this transaction.
- *
- * The sfsync lock should be held before entering this function.
- */
-void
-sf_sync_set_retval(struct sendfile_sync *sfs, off_t retval, int xerrno)
-{
-
- KASSERT(mtx_owned(&sfs->mtx), ("%s: sfs=%p: not locked but should be!",
- __func__,
- sfs));
-
- SFSYNC_DPRINTF("%s: (%llu) sfs=%p: errno=%d, retval=%jd\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs,
- xerrno,
- (intmax_t) retval);
-
- sfs->retval = retval;
- sfs->xerrno = xerrno;
}
/*
@@ -2380,174 +1903,15 @@ sys_sendfile(struct thread *td, struct sendfile_args *uap)
return (do_sendfile(td, uap, 0));
}
-int
-_do_sendfile(struct thread *td, int src_fd, int sock_fd, int flags,
- int compat, off_t offset, size_t nbytes, off_t *sbytes,
- struct uio *hdr_uio,
- struct uio *trl_uio, struct sf_hdtr_kq *hdtr_kq)
-{
- cap_rights_t rights;
- struct sendfile_sync *sfs = NULL;
- struct file *fp;
- int error;
- int do_kqueue = 0;
- int do_free = 0;
-
- AUDIT_ARG_FD(src_fd);
-
- if (hdtr_kq != NULL)
- do_kqueue = 1;
-
- /*
- * sendfile(2) can start at any offset within a file so we require
- * CAP_READ+CAP_SEEK = CAP_PREAD.
- */
- if ((error = fget_read(td, src_fd,
- cap_rights_init(&rights, CAP_PREAD), &fp)) != 0) {
- goto out;
- }
-
- /*
- * IF SF_KQUEUE is set but we haven't copied in anything for
- * kqueue data, error out.
- */
- if (flags & SF_KQUEUE && do_kqueue == 0) {
- SFSYNC_DPRINTF("%s: SF_KQUEUE but no KQUEUE data!\n", __func__);
- goto out;
- }
-
- /*
- * If we need to wait for completion, initialise the sfsync
- * state here.
- */
- if (flags & (SF_SYNC | SF_KQUEUE))
- sfs = sf_sync_alloc(flags & (SF_SYNC | SF_KQUEUE));
-
- if (flags & SF_KQUEUE) {
- error = sf_sync_kqueue_setup(sfs, hdtr_kq);
- if (error) {
- SFSYNC_DPRINTF("%s: (%llu) error; sfs=%p\n",
- __func__,
- (unsigned long long) curthread->td_tid,
- sfs);
- sf_sync_set_state(sfs, SF_STATE_FREEING, 0);
- sf_sync_free(sfs);
- goto out;
- }
- }
-
- /*
- * Do the sendfile call.
- *
- * If this fails, it'll free the mbuf chain which will free up the
- * sendfile_sync references.
- */
- error = fo_sendfile(fp, sock_fd, hdr_uio, trl_uio, offset,
- nbytes, sbytes, flags, compat ? SFK_COMPAT : 0, sfs, td);
-
- /*
- * If the sendfile call succeeded, transition the sf_sync state
- * to RUNNING, then COMPLETED.
- *
- * If the sendfile call failed, then the sendfile call may have
- * actually sent some data first - so we check to see whether
- * any data was sent. If some data was queued (ie, count > 0)
- * then we can't call free; we have to wait until the partial
- * transaction completes before we continue along.
- *
- * This has the side effect of firing off the knote
- * if the refcount has hit zero by the time we get here.
- */
- if (sfs != NULL) {
- mtx_lock(&sfs->mtx);
- if (error == 0 || sfs->count > 0) {
- /*
- * When it's time to do async sendfile, the transition
- * to RUNNING signifies that we're actually actively
- * adding and completing mbufs. When the last disk
- * buffer is read (ie, when we're not doing any
- * further read IO and all subsequent stuff is mbuf
- * transmissions) we'll transition to COMPLETED
- * and when the final mbuf is freed, the completion
- * will be signaled.
- */
- sf_sync_set_state(sfs, SF_STATE_RUNNING, 1);
-
- /*
- * Set the retval before we signal completed.
- * If we do it the other way around then transitioning to
- * COMPLETED may post the knote before you set the return
- * status!
- *
- * XXX for now, errno is always 0, as we don't post
- * knotes if sendfile failed. Maybe that'll change later.
- */
- sf_sync_set_retval(sfs, *sbytes, error);
-
- /*
- * And now transition to completed, which will kick off
- * the knote if required.
- */
- sf_sync_set_state(sfs, SF_STATE_COMPLETED, 1);
- } else {
- /*
- * Error isn't zero, sfs_count is zero, so we
- * won't have some other thing to wake things up.
- * Thus free.
- */
- sf_sync_set_state(sfs, SF_STATE_FREEING, 1);
- do_free = 1;
- }
-
- /*
- * Next - wait if appropriate.
- */
- sf_sync_syscall_wait(sfs);
-
- /*
- * If we're not doing kqueue notifications, we can
- * transition this immediately to the freeing state.
- */
- if ((sfs->flags & SF_KQUEUE) == 0) {
- sf_sync_set_state(sfs, SF_STATE_FREEING, 1);
- do_free = 1;
- }
-
- mtx_unlock(&sfs->mtx);
- }
-
- /*
- * If do_free is set, free here.
- *
- * If we're doing no-kqueue notification and it's just sleep notification,
- * we also do free; it's the only chance we have.
- */
- if (sfs != NULL && do_free == 1) {
- sf_sync_free(sfs);
- }
-
- /*
- * XXX Should we wait until the send has completed before freeing the source
- * file handle? It's the previous behaviour, sure, but is it required?
- * We've wired down the page references after all.
- */
- fdrop(fp, td);
-
-out:
- /* Return error */
- return (error);
-}
-
-
static int
do_sendfile(struct thread *td, struct sendfile_args *uap, int compat)
{
struct sf_hdtr hdtr;
- struct sf_hdtr_kq hdtr_kq;
struct uio *hdr_uio, *trl_uio;
- int error;
+ struct file *fp;
+ cap_rights_t rights;
off_t sbytes;
- int do_kqueue = 0;
+ int error;
/*
* File offset must be positive. If it goes beyond EOF
@@ -2563,38 +1927,37 @@ do_sendfile(struct thread *td, struct sendfile_args *uap, int compat)
if (error != 0)
goto out;
if (hdtr.headers != NULL) {
- error = copyinuio(hdtr.headers, hdtr.hdr_cnt, &hdr_uio);
+ error = copyinuio(hdtr.headers, hdtr.hdr_cnt,
+ &hdr_uio);
if (error != 0)
goto out;
}
if (hdtr.trailers != NULL) {
- error = copyinuio(hdtr.trailers, hdtr.trl_cnt, &trl_uio);
+ error = copyinuio(hdtr.trailers, hdtr.trl_cnt,
+ &trl_uio);
if (error != 0)
goto out;
}
+ }
- /*
- * If SF_KQUEUE is set, then we need to also copy in
- * the kqueue data after the normal hdtr set and set
- * do_kqueue=1.
- */
- if (uap->flags & SF_KQUEUE) {
- error = copyin(((char *) uap->hdtr) + sizeof(hdtr),
- &hdtr_kq,
- sizeof(hdtr_kq));
- if (error != 0)
- goto out;
- do_kqueue = 1;
- }
+ AUDIT_ARG_FD(src_fd);
+
+ /*
+ * sendfile(2) can start at any offset within a file so we require
+ * CAP_READ+CAP_SEEK = CAP_PREAD.
+ */
+ if ((error = fget_read(td, uap->fd,
+ cap_rights_init(&rights, CAP_PREAD), &fp)) != 0) {
+ goto out;
}
- /* Call sendfile */
- error = _do_sendfile(td, uap->fd, uap->s, uap->flags, compat,
- uap->offset, uap->nbytes, &sbytes, hdr_uio, trl_uio, &hdtr_kq);
+ error = fo_sendfile(fp, uap->s, hdr_uio, trl_uio, uap->offset,
+ uap->nbytes, &sbytes, uap->flags, compat ? SFK_COMPAT : 0, td);
+ fdrop(fp, td);
- if (uap->sbytes != NULL) {
+ if (uap->sbytes != NULL)
copyout(&sbytes, uap->sbytes, sizeof(off_t));
- }
+
out:
free(hdr_uio, M_IOV);
free(trl_uio, M_IOV);
@@ -2819,7 +2182,7 @@ kern_sendfile_getsock(struct thread *td, int s, struct file **sock_fp,
int
vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags,
- int kflags, struct sendfile_sync *sfs, struct thread *td)
+ int kflags, struct thread *td)
{
struct file *sock_fp;
struct vnode *vp;
@@ -2829,6 +2192,7 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
struct sf_buf *sf;
struct vm_page *pg;
struct shmfd *shmfd;
+ struct sendfile_sync *sfs;
struct vattr va;
off_t off, xfsize, fsbytes, sbytes, rem, obj_size;
int error, bsize, nd, hdrlen, mnw;
@@ -2837,6 +2201,7 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
obj = NULL;
so = NULL;
m = NULL;
+ sfs = NULL;
fsbytes = sbytes = 0;
hdrlen = mnw = 0;
rem = nbytes;
@@ -2860,6 +2225,12 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
if (flags & SF_MNOWAIT)
mnw = 1;
+ if (flags & SF_SYNC) {
+ sfs = malloc(sizeof *sfs, M_TEMP, M_WAITOK | M_ZERO);
+ mtx_init(&sfs->mtx, "sendfile", NULL, MTX_DEF);
+ cv_init(&sfs->cv, "sendfile");
+ }
+
#ifdef MAC
error = mac_socket_check_send(td->td_ucred, so);
if (error != 0)
@@ -3106,12 +2477,11 @@ retry_space:
loopbytes += xfsize;
off += xfsize;
- /*
- * XXX eventually this should be a sfsync
- * method call!
- */
- if (sfs != NULL)
- sf_sync_ref(sfs);
+ if (sfs != NULL) {
+ mtx_lock(&sfs->mtx);
+ sfs->count++;
+ mtx_unlock(&sfs->mtx);
+ }
}
if (vp != NULL)
@@ -3193,6 +2563,16 @@ out:
if (m)
m_freem(m);
+ if (sfs != NULL) {
+ mtx_lock(&sfs->mtx);
+ if (sfs->count != 0)
+ cv_wait(&sfs->cv, &sfs->mtx);
+ KASSERT(sfs->count == 0, ("sendfile sync still busy"));
+ cv_destroy(&sfs->cv);
+ mtx_destroy(&sfs->mtx);
+ free(sfs, M_TEMP);
+ }
+
if (error == ERESTART)
error = EINTR;
diff --git a/sys/sys/file.h b/sys/sys/file.h
index e593d4326240..d102a871ee39 100644
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -90,9 +90,6 @@ foffset_get(struct file *fp)
return (foffset_lock(fp, FOF_NOLOCK));
}
-/* XXX pollution? */
-struct sendfile_sync;
-
typedef int fo_rdwr_t(struct file *fp, struct uio *uio,
struct ucred *active_cred, int flags,
struct thread *td);
@@ -112,8 +109,7 @@ typedef int fo_chown_t(struct file *fp, uid_t uid, gid_t gid,
struct ucred *active_cred, struct thread *td);
typedef int fo_sendfile_t(struct file *fp, int sockfd, struct uio *hdr_uio,
struct uio *trl_uio, off_t offset, size_t nbytes,
- off_t *sent, int flags, int kflags,
- struct sendfile_sync *sfs, struct thread *td);
+ off_t *sent, int flags, int kflags, struct thread *td);
typedef int fo_seek_t(struct file *fp, off_t offset, int whence,
struct thread *td);
typedef int fo_fill_kinfo_t(struct file *fp, struct kinfo_file *kif,
@@ -371,11 +367,11 @@ fo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
static __inline int
fo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
struct uio *trl_uio, off_t offset, size_t nbytes, off_t *sent, int flags,
- int kflags, struct sendfile_sync *sfs, struct thread *td)
+ int kflags, struct thread *td)
{
return ((*fp->f_ops->fo_sendfile)(fp, sockfd, hdr_uio, trl_uio, offset,
- nbytes, sent, flags, kflags, sfs, td));
+ nbytes, sent, flags, kflags, td));
}
static __inline int
diff --git a/sys/sys/sf_base.h b/sys/sys/sf_base.h
deleted file mode 100644
index 7c8d49cde4fe..000000000000
--- a/sys/sys/sf_base.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*-
- * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
-
-#ifndef _SYS_SF_BASE_H_
-#define _SYS_SF_BASE_H_
-
-extern int _do_sendfile(struct thread *, int src_fd, int sock_fd, int flags,
- int compat, off_t offset, size_t nbytes, off_t *sbytes,
- struct uio *hdr_uio, struct uio *trl_uio,
- struct sf_hdtr_kq *hdtr_kq);
-
-#endif /* _SYS_SF_BASE_H_ */
diff --git a/sys/sys/sf_sync.h b/sys/sys/sf_sync.h
deleted file mode 100644
index 04dee3801a5e..000000000000
--- a/sys/sys/sf_sync.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*-
- * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
-
-#ifndef _SYS_SF_SYNC_H_
-#define _SYS_SF_SYNC_H_
-
-typedef enum {
- SF_STATE_NONE,
- SF_STATE_SETUP,
- SF_STATE_RUNNING,
- SF_STATE_COMPLETED,
- SF_STATE_FREEING
-} sendfile_sync_state_t;
-
-struct sendfile_sync {
- struct mtx mtx;
- struct cv cv;
- struct knlist klist;
- uint32_t flags;
- uint32_t count;
- int32_t xerrno; /* Completion errno, if retval < 0 */
- off_t retval; /* Completion retval (eg written bytes) */
- sendfile_sync_state_t state;
-};
-
-/* XXX pollution */
-struct sf_hdtr_kq;
-
-extern struct sendfile_sync * sf_sync_alloc(uint32_t flags);
-extern void sf_sync_syscall_wait(struct sendfile_sync *);
-extern void sf_sync_free(struct sendfile_sync *);
-extern void sf_sync_try_free(struct sendfile_sync *);
-extern void sf_sync_ref(struct sendfile_sync *);
-extern void sf_sync_deref(struct sendfile_sync *);
-extern int sf_sync_kqueue_setup(struct sendfile_sync *, struct sf_hdtr_kq *);
-extern void sf_sync_set_state(struct sendfile_sync *, sendfile_sync_state_t, int);
-extern void sf_sync_set_retval(struct sendfile_sync *, off_t, int);
-
-#endif /* !_SYS_SF_BUF_H_ */
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
index cc43fa21dc09..18e2de10e101 100644
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -584,27 +584,11 @@ struct sf_hdtr {
};
/*
- * sendfile(2) kqueue information
- */
-struct sf_hdtr_kq {
- uintptr_t kq_ident; /* ident (from userland?) */
- void *kq_udata; /* user data pointer */
- uint32_t kq_flags; /* extra flags to pass in */
- int kq_fd; /* kq fd to post completion events on */
-};
-
-struct sf_hdtr_all {
- struct sf_hdtr hdtr;
- struct sf_hdtr_kq kq;
-};
-
-/*
* Sendfile-specific flag(s)
*/
#define SF_NODISKIO 0x00000001
#define SF_MNOWAIT 0x00000002
#define SF_SYNC 0x00000004
-#define SF_KQUEUE 0x00000008
#ifdef _KERNEL
#define SFK_COMPAT 0x00000001