sockets: provide sousrsend() that does socket specific error handling
Sockets have special handling for EPIPE on a write, that was spread out
into several places. Treating transient errors is also special - if
protocol is atomic, than we should ignore any changes to uio_resid, a
transient error means the write had completely failed (see d2b3a0ed31
).
- Provide sousrsend() that expects a valid uio, and leave sosend() for
kernel consumers only. Do all special error handling right here.
- In dofilewrite() don't do special handling of error for DTYPE_SOCKET.
- For send(2), write(2) and aio_write(2) call into sousrsend() and remove
error handling for kern_sendit(), soo_write() and soaio_process_job().
PR: 265087
Reported by: rz-rpi03 at h-ka.de
Reviewed by: markj
Differential revision: https://reviews.freebsd.org/D35863
This commit is contained in:
parent
eaabc93764
commit
7a2c93b86e
@ -562,12 +562,16 @@ dofilewrite(struct thread *td, int fd, struct file *fp, struct uio *auio,
|
|||||||
ktruio = cloneuio(auio);
|
ktruio = cloneuio(auio);
|
||||||
#endif
|
#endif
|
||||||
cnt = auio->uio_resid;
|
cnt = auio->uio_resid;
|
||||||
if ((error = fo_write(fp, auio, td->td_ucred, flags, td))) {
|
error = fo_write(fp, auio, td->td_ucred, flags, td);
|
||||||
|
/*
|
||||||
|
* Socket layer is responsible for special error handling,
|
||||||
|
* see sousrsend().
|
||||||
|
*/
|
||||||
|
if (error != 0 && fp->f_type != DTYPE_SOCKET) {
|
||||||
if (auio->uio_resid != cnt && (error == ERESTART ||
|
if (auio->uio_resid != cnt && (error == ERESTART ||
|
||||||
error == EINTR || error == EWOULDBLOCK))
|
error == EINTR || error == EWOULDBLOCK))
|
||||||
error = 0;
|
error = 0;
|
||||||
/* Socket layer is responsible for issuing SIGPIPE. */
|
if (error == EPIPE) {
|
||||||
if (fp->f_type != DTYPE_SOCKET && error == EPIPE) {
|
|
||||||
PROC_LOCK(td->td_proc);
|
PROC_LOCK(td->td_proc);
|
||||||
tdsignal(td, SIGPIPE);
|
tdsignal(td, SIGPIPE);
|
||||||
PROC_UNLOCK(td->td_proc);
|
PROC_UNLOCK(td->td_proc);
|
||||||
|
@ -145,13 +145,7 @@ soo_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
|
|||||||
if (error)
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
#endif
|
#endif
|
||||||
error = sosend(so, 0, uio, 0, 0, 0, uio->uio_td);
|
return (sousrsend(so, NULL, uio, NULL, 0, NULL));
|
||||||
if (error == EPIPE && (so->so_options & SO_NOSIGPIPE) == 0) {
|
|
||||||
PROC_LOCK(uio->uio_td->td_proc);
|
|
||||||
tdsignal(uio->uio_td, SIGPIPE);
|
|
||||||
PROC_UNLOCK(uio->uio_td->td_proc);
|
|
||||||
}
|
|
||||||
return (error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -646,15 +640,10 @@ soaio_process_job(struct socket *so, sb_which which, struct kaiocb *job)
|
|||||||
error = mac_socket_check_send(fp->f_cred, so);
|
error = mac_socket_check_send(fp->f_cred, so);
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
#endif
|
#endif
|
||||||
error = sosend(so, NULL, job->uiop, NULL, NULL, flags,
|
error = sousrsend(so, NULL, job->uiop, NULL, flags,
|
||||||
td);
|
job->userproc);
|
||||||
if (td->td_ru.ru_msgsnd != ru_before)
|
if (td->td_ru.ru_msgsnd != ru_before)
|
||||||
job->msgsnd = 1;
|
job->msgsnd = 1;
|
||||||
if (error == EPIPE && (so->so_options & SO_NOSIGPIPE) == 0) {
|
|
||||||
PROC_LOCK(job->userproc);
|
|
||||||
kern_psignal(job->userproc, SIGPIPE);
|
|
||||||
PROC_UNLOCK(job->userproc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
done += cnt - job->uiop->uio_resid;
|
done += cnt - job->uiop->uio_resid;
|
||||||
|
@ -1822,6 +1822,14 @@ sosend_generic(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
|||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send to a socket from a kernel thread.
|
||||||
|
*
|
||||||
|
* XXXGL: in almost all cases uio is NULL and the mbuf is supplied.
|
||||||
|
* Exception is nfs/bootp_subr.c. It is arguable that the VNET context needs
|
||||||
|
* to be set at all. This function should just boil down to a static inline
|
||||||
|
* calling the protocol method.
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
sosend(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
sosend(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
||||||
struct mbuf *top, struct mbuf *control, int flags, struct thread *td)
|
struct mbuf *top, struct mbuf *control, int flags, struct thread *td)
|
||||||
@ -1835,6 +1843,47 @@ sosend(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
|||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* send(2), write(2) or aio_write(2) on a socket.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sousrsend(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
||||||
|
struct mbuf *control, int flags, struct proc *userproc)
|
||||||
|
{
|
||||||
|
struct thread *td;
|
||||||
|
ssize_t len;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
td = uio->uio_td;
|
||||||
|
len = uio->uio_resid;
|
||||||
|
CURVNET_SET(so->so_vnet);
|
||||||
|
error = so->so_proto->pr_sosend(so, addr, uio, NULL, control, flags,
|
||||||
|
td);
|
||||||
|
CURVNET_RESTORE();
|
||||||
|
if (error != 0) {
|
||||||
|
if (uio->uio_resid != len &&
|
||||||
|
(so->so_proto->pr_flags & PR_ATOMIC) == 0 &&
|
||||||
|
(error == ERESTART || error == EINTR ||
|
||||||
|
error == EWOULDBLOCK))
|
||||||
|
error = 0;
|
||||||
|
/* Generation of SIGPIPE can be controlled per socket. */
|
||||||
|
if (error == EPIPE && (so->so_options & SO_NOSIGPIPE) == 0 &&
|
||||||
|
(flags & MSG_NOSIGNAL) == 0) {
|
||||||
|
if (userproc != NULL) {
|
||||||
|
/* aio(4) job */
|
||||||
|
PROC_LOCK(userproc);
|
||||||
|
kern_psignal(userproc, SIGPIPE);
|
||||||
|
PROC_UNLOCK(userproc);
|
||||||
|
} else {
|
||||||
|
PROC_LOCK(td->td_proc);
|
||||||
|
tdsignal(td, SIGPIPE);
|
||||||
|
PROC_UNLOCK(td->td_proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The part of soreceive() that implements reading non-inline out-of-band
|
* The part of soreceive() that implements reading non-inline out-of-band
|
||||||
* data from a socket. For more complete comments, see soreceive(), from
|
* data from a socket. For more complete comments, see soreceive(), from
|
||||||
|
@ -798,21 +798,7 @@ kern_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
|
|||||||
ktruio = cloneuio(&auio);
|
ktruio = cloneuio(&auio);
|
||||||
#endif
|
#endif
|
||||||
len = auio.uio_resid;
|
len = auio.uio_resid;
|
||||||
error = sosend(so, mp->msg_name, &auio, 0, control, flags, td);
|
error = sousrsend(so, mp->msg_name, &auio, control, flags, NULL);
|
||||||
if (error != 0) {
|
|
||||||
if (auio.uio_resid != len &&
|
|
||||||
(so->so_proto->pr_flags & PR_ATOMIC) == 0 &&
|
|
||||||
(error == ERESTART || error == EINTR ||
|
|
||||||
error == EWOULDBLOCK))
|
|
||||||
error = 0;
|
|
||||||
/* Generation of SIGPIPE can be controlled per socket */
|
|
||||||
if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) &&
|
|
||||||
!(flags & MSG_NOSIGNAL)) {
|
|
||||||
PROC_LOCK(td->td_proc);
|
|
||||||
tdsignal(td, SIGPIPE);
|
|
||||||
PROC_UNLOCK(td->td_proc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
td->td_retval[0] = len - auio.uio_resid;
|
td->td_retval[0] = len - auio.uio_resid;
|
||||||
#ifdef KTRACE
|
#ifdef KTRACE
|
||||||
|
@ -506,6 +506,8 @@ void sorflush(struct socket *so);
|
|||||||
int sosend(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
int sosend(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
||||||
struct mbuf *top, struct mbuf *control, int flags,
|
struct mbuf *top, struct mbuf *control, int flags,
|
||||||
struct thread *td);
|
struct thread *td);
|
||||||
|
int sousrsend(struct socket *so, struct sockaddr *addr, struct uio *uio,
|
||||||
|
struct mbuf *control, int flags, struct proc *);
|
||||||
int sosend_dgram(struct socket *so, struct sockaddr *addr,
|
int sosend_dgram(struct socket *so, struct sockaddr *addr,
|
||||||
struct uio *uio, struct mbuf *top, struct mbuf *control,
|
struct uio *uio, struct mbuf *top, struct mbuf *control,
|
||||||
int flags, struct thread *td);
|
int flags, struct thread *td);
|
||||||
|
Loading…
Reference in New Issue
Block a user