diff options
Diffstat (limited to 'pflocal')
-rw-r--r-- | pflocal/sock.c | 163 |
1 files changed, 93 insertions, 70 deletions
diff --git a/pflocal/sock.c b/pflocal/sock.c index 925f8187..3f281d35 100644 --- a/pflocal/sock.c +++ b/pflocal/sock.c @@ -23,65 +23,10 @@ #include <cthreads.h> #include "sock.h" +#include "sserver.h" #include "pipe.h" - -/* ---------------------------------------------------------------- */ - -/* A port bucket to handle SOCK_USERs and ADDRs. */ -static struct port_bucket *sock_port_bucket; - -/* True if there are threads servicing sock requests. */ -static int sock_server_active = 0; -static spin_lock_t sock_server_active_lock = SPIN_LOCK_INITIALIZER; - -/* A demuxer for socket operations. */ -static int -sock_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) -{ - extern int socket_server (mach_msg_header_t *inp, mach_msg_header_t *outp); - extern int io_server (mach_msg_header_t *inp, mach_msg_header_t *outp); - return socket_server (inp, outp) || io_server (inp, outp); -} -/* Handle socket requests while there are sockets around. */ -static void -handle_sock_requests () -{ - while (ports_count_bucket (sock_port_bucket) > 0) - { - ports_enable_bucket (sock_port_bucket); - ports_manage_port_operations_multithread (sock_port_bucket, sock_demuxer, - 30*1000, 2*60*1000, - 1, MACH_PORT_NULL); - } - - /* The last service thread is about to exist; make this known. */ - spin_lock (&sock_server_active_lock); - sock_server_active = 0; - spin_unlock (&sock_server_active_lock); - - /* Let the whole joke start once again. */ - ports_enable_bucket (sock_port_bucket); -} - -/* Makes sure there are some request threads for sock operations, and starts - a server if necessary. This routine should be called *after* creating the - port(s) which need server, as the server routine only operates while there - are any ports. */ -static void -ensure_sock_server () -{ - spin_lock (&sock_server_active_lock); - if (sock_server_active) - spin_unlock (&sock_server_active_lock); - else - { - sock_server_active = 1; - spin_unlock (&sock_server_active_lock); - cthread_detach (cthread_fork ((cthread_fn_t)handle_sock_requests, - (any_t)0)); - } -} +#include "debug.h" /* ---------------------------------------------------------------- */ @@ -92,7 +37,9 @@ error_t sock_aquire_read_pipe (struct sock *sock, struct pipe **pipe) { error_t err = 0; +debug (sock, "in"); +debug (sock, "lock"); mutex_lock (&sock->lock); *pipe = sock->read_pipe; @@ -104,12 +51,16 @@ sock_aquire_read_pipe (struct sock *sock, struct pipe **pipe) /* A broken pipe with no peer is not connected (only connection-oriented sockets can have broken pipes. However this is not true if the read-half has been explicitly shutdown [at least in netbsd]. */ +{debug (sock, "enotconn"); err = ENOTCONN; +} else pipe_aquire (*pipe); +debug (sock, "unlock"); mutex_unlock (&sock->lock); +debug (sock, "out"); return err; } @@ -120,7 +71,9 @@ error_t sock_aquire_write_pipe (struct sock *sock, struct pipe **pipe) { error_t err = 0; +debug (sock, "in"); +debug (sock, "lock"); mutex_lock (&sock->lock); *pipe = sock->write_pipe; if (*pipe != NULL) @@ -129,14 +82,22 @@ sock_aquire_write_pipe (struct sock *sock, struct pipe **pipe) /* Writing on a socket with the write-half shutdown always acts as if the pipe were broken, even if the socket isn't connected yet [at least in netbsd]. */ +{debug (sock, "epipe"); err = EPIPE; +} else if (sock->read_pipe->class->flags & PIPE_CLASS_CONNECTIONLESS) /* Connectionless protocols give a different error when unconnected. */ +{debug (sock, "edestaddrreq"); err = EDESTADDRREQ; +} else +{debug (sock, "enotconn"); err = ENOTCONN; +} +debug (sock, "unlock"); mutex_unlock (&sock->lock); +debug (sock, "out"); return err; } @@ -182,14 +143,27 @@ sock_create (struct pipe_class *pipe_class, struct sock **sock) void sock_free (struct sock *sock) { +debug (sock, "in"); /* sock_shutdown will get rid of the write pipe. */ sock_shutdown (sock, SOCK_SHUTDOWN_READ | SOCK_SHUTDOWN_WRITE); /* But we must do the read pipe ourselves. */ pipe_release (sock->read_pipe); +debug (sock, "bye"); free (sock); } + +/* Free a sock derefed too far. */ +void +_sock_norefs (struct sock *sock) +{ + /* A sock should never have an address when it has 0 refs, as the + address should hold a reference to the sock! */ + assert (sock->addr == NULL); + mutex_unlock (&sock->lock); /* Unlock so sock_free can do stuff. */ + sock_free (sock); +} /* ---------------------------------------------------------------- */ @@ -214,9 +188,10 @@ struct port_class *sock_user_port_class; /* Get rid of a user reference to a socket. */ static void -clean_sock_user (void *vuser) +sock_user_clean (void *vuser) { struct sock_user *user = vuser; +debug (user, "bye"); sock_deref (user->sock); } @@ -233,13 +208,17 @@ sock_create_port (struct sock *sock, mach_port_t *port) ensure_sock_server (); +debug (sock, "lock, refs++"); mutex_lock (&sock->lock); sock->refs++; +debug (sock, "unlock"); mutex_unlock (&sock->lock); user->sock = sock; *port = ports_get_right (user); + ports_port_deref (user); /* We only want one ref, for the send right. */ +debug (sock, "user: %p, refs: %d", user, user->pi.refcnt); return 0; } @@ -259,33 +238,40 @@ struct port_class *addr_port_class; /* Get rid of ADDR's socket's reference to it, in preparation for ADDR going away. */ static void -unbind_addr (void *vaddr) +addr_unbind (void *vaddr) { struct sock *sock; struct addr *addr = vaddr; +debug (addr, "in"); +debug (addr, "lock"); mutex_lock (&addr->lock); sock = addr->sock; if (sock) { +debug (sock, "(sock) lock"); mutex_lock (&sock->lock); sock->addr = NULL; addr->sock = NULL; ports_port_deref_weak (addr); +debug (sock, "(sock) unlock"); mutex_unlock (&sock->lock); sock_deref (sock); } +debug (addr, "unlock"); mutex_unlock (&addr->lock); +debug (addr, "out"); } /* Cleanup after the address ADDR, which is going away... */ static void -clean_addr (void *vaddr) +addr_clean (void *vaddr) { struct addr *addr = vaddr; /* ADDR should never have a socket bound to it at this point, as it should - have been removed by unbind_addr dropping the socket's weak reference + have been removed by addr_unbind dropping the socket's weak reference it. */ +debug (addr, "bye"); assert (addr->sock == NULL); free (addr); } @@ -313,7 +299,9 @@ sock_bind (struct sock *sock, struct addr *addr) error_t err = 0; struct addr *old_addr; +debug (addr, "(addr) lock"); mutex_lock (&addr->lock); +debug (sock, "lock"); mutex_lock (&sock->lock); old_addr = sock->addr; @@ -330,16 +318,21 @@ sock_bind (struct sock *sock, struct addr *addr) sock->addr = addr; if (addr) +{debug (sock, "refs++"); sock->refs++; +} if (old_addr) { /* Note that we don't have to worry about SOCK's ref count going to zero because whoever's calling us should be holding a ref somehow. */ +debug (sock, "refs--"); sock->refs--; assert (sock->refs > 0); /* But make sure... */ } +debug (sock, "unlock"); mutex_unlock (&sock->lock); +debug (addr, "(addr) unlock"); mutex_unlock (&addr->lock); return err; @@ -362,12 +355,11 @@ ensure_addr (struct sock *sock, struct addr **addr) ports_port_ref_weak (sock->addr); } } + else + ports_port_ref (sock->addr); if (!err) - { - *addr = sock->addr; - ports_port_ref (*addr); - } + *addr = sock->addr; return err; } @@ -377,10 +369,12 @@ ensure_addr (struct sock *sock, struct addr **addr) error_t addr_get_sock (struct addr *addr, struct sock **sock) { +debug (addr, "lock"); mutex_lock (&addr->lock); *sock = addr->sock; if (*sock) (*sock)->refs++; +debug (addr, "unlock"); mutex_unlock (&addr->lock); return *sock ? 0 : EADDRNOTAVAIL; } @@ -392,8 +386,10 @@ sock_get_addr (struct sock *sock, struct addr **addr) { error_t err; +debug (sock, "lock"); mutex_lock (&sock->lock); err = ensure_addr (sock, addr); +debug (sock, "unlock"); mutex_unlock (&sock->lock); return err; /* XXX */ @@ -425,24 +421,34 @@ sock_connect (struct sock *sock1, struct sock *sock2) || (rd->flags & SOCK_SHUTDOWN_READ))) { struct pipe *pipe = rd->read_pipe; +debug (wr, "connect: %p, pipe: %p", rd, pipe); pipe_aquire (pipe); pipe->flags &= ~PIPE_BROKEN; /* Not yet... */ wr->write_pipe = pipe; +debug (pipe, "(pipe) unlock"); mutex_unlock (&pipe->lock); } } +debug (sock1, "in: %p", sock2); if (sock2->read_pipe->class != pipe_class) /* Incompatible socket types. */ +{debug (sock1, "eopnotsupp"); return EOPNOTSUPP; /* XXX?? */ +} +debug (sock1, "socket pair lock"); mutex_lock (&socket_pair_lock); +debug (sock1, "lock"); mutex_lock (&sock1->lock); +debug (sock2, "lock"); mutex_lock (&sock2->lock); if ((sock1->flags & SOCK_CONNECTED) || (sock2->flags & SOCK_CONNECTED)) /* An already-connected socket. */ +{debug (sock1, "eisconn"); err = EISCONN; +} else { old_sock1_write_pipe = sock1->write_pipe; @@ -460,8 +466,11 @@ sock_connect (struct sock *sock1, struct sock *sock2) } } +debug (sock2, "unlock"); mutex_unlock (&sock2->lock); +debug (sock1, "unlock"); mutex_unlock (&sock1->lock); +debug (sock1, "socket pair unlock"); mutex_unlock (&socket_pair_lock); if (old_sock1_write_pipe) @@ -470,6 +479,7 @@ sock_connect (struct sock *sock1, struct sock *sock2) ports_port_deref (old_sock1_write_addr); } +debug (sock1, "out"); return err; } @@ -480,6 +490,8 @@ sock_connect (struct sock *sock1, struct sock *sock2) void sock_shutdown (struct sock *sock, unsigned flags) { +debug (sock, "in"); +debug (sock, "lock"); mutex_lock (&sock->lock); sock->flags |= flags; @@ -488,11 +500,14 @@ sock_shutdown (struct sock *sock, unsigned flags) /* Shutdown the read half. We keep the pipe around though. */ { struct pipe *pipe = sock->read_pipe; +debug (sock, "read half"); +debug (pipe, "(pipe) lock"); mutex_lock (&pipe->lock); /* This will prevent any further writes to PIPE. */ pipe->flags |= PIPE_BROKEN; /* Make sure subsequent reads return EOF. */ pipe_drain (pipe); +debug (pipe, "(pipe) unlock"); mutex_unlock (&pipe->lock); } @@ -500,19 +515,26 @@ sock_shutdown (struct sock *sock, unsigned flags) /* Shutdown the write half. */ { struct pipe *pipe = sock->write_pipe; +debug (sock, "write half"); if (pipe != NULL) { sock->write_pipe = NULL; /* Unlock SOCK here, as we may subsequently wake up other threads. */ +debug (sock, "unlock"); mutex_unlock (&sock->lock); pipe_break (pipe); } else +{debug (sock, "unlock"); mutex_unlock (&sock->lock); +} } else +{debug (sock, "unlock"); mutex_unlock (&sock->lock); } +debug (sock, "out"); +} /* ---------------------------------------------------------------- */ @@ -520,15 +542,16 @@ error_t sock_global_init () { sock_port_bucket = ports_create_bucket (); - sock_user_port_class = ports_create_class (NULL, clean_sock_user); - addr_port_class = ports_create_class (unbind_addr, clean_addr); + sock_user_port_class = ports_create_class (sock_user_clean, NULL); + addr_port_class = ports_create_class (addr_clean, addr_unbind); return 0; } /* Try to shutdown any active sockets, returning EBUSY if we can't. */ error_t -sock_global_shutdown (int flags) +sock_global_shutdown () { - /* XXX */ - return 0; + int num_ports = ports_count_bucket (sock_user_bucket); + ports_enable_bucket (sock_user_bucket); + return (num_ports == 0 ? 0 : EBUSY); } |