#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/stropts.h>
#include <sys/socketvar.h>
#include <sys/ksocket.h>
#include <io/ksocket/ksocket_impl.h>
#include <fs/sockfs/sockcommon.h>
#include <fs/sockfs/sodirect.h>
#include <fs/sockfs/sockfilter_impl.h>
#define SO_WAKEUP_READER(so) { \
if ((so)->so_rcv_wakeup) { \
(so)->so_rcv_wakeup = B_FALSE; \
cv_signal(&(so)->so_rcv_cv); \
} \
}
#define SO_WAKEUP_WRITER(so) { \
if ((so)->so_snd_wakeup) { \
(so)->so_snd_wakeup = B_FALSE; \
cv_broadcast(&(so)->so_snd_cv); \
} \
}
static int i_so_notify_last_rx(struct sonode *, int *, int *);
static int i_so_notify_last_tx(struct sonode *, int *, int *);
void
so_notify_connected(struct sonode *so)
{
ASSERT(MUTEX_HELD(&so->so_lock));
if (IS_KERNEL_SOCKET(so)) {
KSOCKET_CALLBACK(so, connected, 0);
mutex_exit(&so->so_lock);
} else {
socket_sendsig(so, SOCKETSIG_WRITE);
mutex_exit(&so->so_lock);
pollwakeup(&so->so_poll_list, POLLOUT);
}
sof_sonode_notify_filters(so, SOF_EV_CONNECTED, 0);
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
}
void
so_notify_disconnecting(struct sonode *so)
{
int pollev = 0;
int sigev = 0;
ASSERT(MUTEX_HELD(&so->so_lock));
(void) i_so_notify_last_tx(so, &pollev, &sigev);
if (IS_KERNEL_SOCKET(so)) {
KSOCKET_CALLBACK(so, cantsendmore, 0);
mutex_exit(&so->so_lock);
} else {
if (sigev != 0)
socket_sendsig(so, sigev);
mutex_exit(&so->so_lock);
if (pollev != 0)
pollwakeup(&so->so_poll_list, pollev);
}
sof_sonode_notify_filters(so, SOF_EV_CANTSENDMORE, 0);
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
}
void
so_notify_disconnected(struct sonode *so, boolean_t connfailed, int error)
{
int pollev = 0;
int sigev = 0;
ASSERT(MUTEX_HELD(&so->so_lock));
(void) i_so_notify_last_tx(so, &pollev, &sigev);
(void) i_so_notify_last_rx(so, &pollev, &sigev);
if (IS_KERNEL_SOCKET(so)) {
if (connfailed) {
KSOCKET_CALLBACK(so, disconnected, error);
} else {
KSOCKET_CALLBACK(so, connectfailed, error);
}
mutex_exit(&so->so_lock);
} else {
if (sigev != 0)
socket_sendsig(so, sigev);
mutex_exit(&so->so_lock);
if (!connfailed)
pollev = (pollev | POLLHUP) & ~POLLOUT;
if (pollev != 0)
pollwakeup(&so->so_poll_list, pollev);
}
sof_sonode_notify_filters(so, (connfailed) ? SOF_EV_CONNECTFAILED :
SOF_EV_DISCONNECTED, error);
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
}
void
so_notify_writable(struct sonode *so)
{
ASSERT(MUTEX_HELD(&so->so_lock));
SO_WAKEUP_WRITER(so);
if (IS_KERNEL_SOCKET(so)) {
KSOCKET_CALLBACK(so, cansend, 0);
mutex_exit(&so->so_lock);
} else {
socket_sendsig(so, SOCKETSIG_WRITE);
mutex_exit(&so->so_lock);
pollwakeup(&so->so_poll_list, POLLOUT);
}
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
if (so->so_filter_active > 0)
sof_sonode_notify_filters(so, SOF_EV_INJECT_DATA_OUT_OK, 0);
}
void
so_notify_data(struct sonode *so, size_t qlen)
{
ASSERT(MUTEX_HELD(&so->so_lock));
SO_WAKEUP_READER(so);
if (IS_KERNEL_SOCKET(so)) {
KSOCKET_CALLBACK(so, newdata, qlen);
mutex_exit(&so->so_lock);
} else {
socket_sendsig(so, SOCKETSIG_READ);
if (so->so_pollev & (SO_POLLEV_IN|SO_POLLEV_ALWAYS)) {
so->so_pollev &= ~SO_POLLEV_IN;
mutex_exit(&so->so_lock);
pollwakeup(&so->so_poll_list, POLLIN|POLLRDNORM);
} else {
mutex_exit(&so->so_lock);
}
}
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
}
void
so_notify_error(struct sonode *so)
{
ASSERT(MUTEX_HELD(&so->so_lock));
SO_WAKEUP_WRITER(so);
SO_WAKEUP_READER(so);
if (IS_KERNEL_SOCKET(so)) {
KSOCKET_CALLBACK(so, error, 0);
mutex_exit(&so->so_lock);
} else {
socket_sendsig(so, SOCKETSIG_WRITE|SOCKETSIG_READ);
so->so_pollev &= ~SO_POLLEV_IN;
mutex_exit(&so->so_lock);
pollwakeup(&so->so_poll_list, POLLOUT|POLLIN|POLLRDNORM);
}
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
}
void
so_notify_oobsig(struct sonode *so)
{
socket_sendsig(so, SOCKETSIG_URG);
mutex_exit(&so->so_lock);
pollwakeup(&so->so_poll_list, POLLRDBAND);
}
void
so_notify_oobdata(struct sonode *so, boolean_t oob_inline)
{
ASSERT(MUTEX_HELD(&so->so_lock));
if (so->so_direct != NULL)
SOD_UIOAFINI(so->so_direct);
SO_WAKEUP_READER(so);
if (IS_KERNEL_SOCKET(so)) {
KSOCKET_CALLBACK(so, oobdata, 0);
mutex_exit(&so->so_lock);
} else {
if (oob_inline) {
socket_sendsig(so, SOCKETSIG_READ);
so->so_pollev &= ~SO_POLLEV_IN;
mutex_exit(&so->so_lock);
pollwakeup(&so->so_poll_list,
POLLRDBAND|POLLIN|POLLRDNORM);
} else {
mutex_exit(&so->so_lock);
pollwakeup(&so->so_poll_list, POLLRDBAND);
}
}
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
}
void
so_notify_eof(struct sonode *so)
{
int pollev = 0;
int sigev = 0;
ASSERT(MUTEX_HELD(&so->so_lock));
(void) i_so_notify_last_rx(so, &pollev, &sigev);
if (IS_KERNEL_SOCKET(so)) {
KSOCKET_CALLBACK(so, cantrecvmore, 0);
mutex_exit(&so->so_lock);
} else {
if (sigev != 0)
socket_sendsig(so, sigev);
mutex_exit(&so->so_lock);
if (pollev != 0)
pollwakeup(&so->so_poll_list, pollev);
}
sof_sonode_notify_filters(so, SOF_EV_CANTRECVMORE, 0);
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
}
void
so_notify_newconn(struct sonode *so)
{
ASSERT(MUTEX_HELD(&so->so_lock));
if (IS_KERNEL_SOCKET(so)) {
KSOCKET_CALLBACK(so, newconn, 0);
mutex_exit(&so->so_lock);
} else {
socket_sendsig(so, SOCKETSIG_READ);
if (so->so_pollev & (SO_POLLEV_IN|SO_POLLEV_ALWAYS)) {
so->so_pollev &= ~SO_POLLEV_IN;
mutex_exit(&so->so_lock);
pollwakeup(&so->so_poll_list, POLLIN|POLLRDNORM);
} else {
mutex_exit(&so->so_lock);
}
}
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
}
void
so_notify_shutdown(struct sonode *so)
{
int pollev = 0;
int sigev = 0;
ASSERT(MUTEX_HELD(&so->so_lock));
ASSERT(so->so_state & (SS_CANTSENDMORE|SS_CANTRCVMORE));
if (so->so_state & SS_CANTSENDMORE)
(void) i_so_notify_last_tx(so, &pollev, &sigev);
if (so->so_state & SS_CANTRCVMORE)
(void) i_so_notify_last_rx(so, &pollev, &sigev);
if (sigev != 0)
socket_sendsig(so, sigev);
mutex_exit(&so->so_lock);
if (pollev != 0)
pollwakeup(&so->so_poll_list, pollev);
ASSERT(MUTEX_NOT_HELD(&so->so_lock));
}
static int
i_so_notify_last_rx(struct sonode *so, int *pollev, int *sigev)
{
if (!(so->so_state & SS_SENTLASTREADSIG)) {
SOCKET_TIMER_CANCEL(so);
SO_WAKEUP_READER(so);
so->so_state |= SS_SENTLASTREADSIG;
so->so_pollev &= ~SO_POLLEV_IN;
*pollev |= POLLIN|POLLRDNORM|POLLRDHUP;
*sigev |= SOCKETSIG_READ;
return (1);
} else {
return (0);
}
}
static int
i_so_notify_last_tx(struct sonode *so, int *pollev, int *sigev)
{
if (!(so->so_state & SS_SENTLASTWRITESIG)) {
SO_WAKEUP_WRITER(so);
so->so_state |= SS_SENTLASTWRITESIG;
*pollev |= POLLOUT;
*sigev |= SOCKETSIG_WRITE;
return (1);
} else {
return (0);
}
}