accept: Save foreign address earlier, if protocol supports it
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Tue, 29 Nov 2011 13:38:34 +0000 (21:38 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Tue, 29 Nov 2011 14:12:44 +0000 (22:12 +0800)
commit88da6203404cc737cdbc49d09c95a3ea552ef7e3
treeb8440b54246b0d987022c2f8e0ae22bf88bcd0f4
parent2af9d75dac792301c2672459217b4b91c60a1d1f
accept: Save foreign address earlier, if protocol supports it

- Add so_faddr into socket, which records the accepted socket's foreign
  address.  If it is set, kern_accept() will use it directly instead of
  calling protocol specific method to extract the foreign address.
- Add protocol specific method, pru_safefaddr, which will save the
  foreign address into socket.so_faddr if the necessary information is
  supplied.  This protocol method will only be called in protocol
  thread.
- Pass the foreign address to sonewconn() if possible, so the foreign
  address could be saved before the accepted socket is put onto the
  complete list.

Currently only IPv4/TCP implemented pru_savefaddr

This intends to address the following problems:
- Calling pru_accept directly from user context is not MPSAFE, we
  always races the socket.so_pcb check->use against protocol thread
  clear/free socket.so_pcb, though the race window is too tiny to
  be hit.  To make it mpsafe, we should dispatch pru_accept to
  protocol thread.
  If socket.so_faddr is set here, we are race against nothing and
  nothing expensive like put the current user thread into sleep will
  happen.  However, if the socket is dropped when it still sits
  on the complete list, the error will not be timely delivered, i.e.
  accept(2) will not return error, but the later on read(2)/write(2)
  on the socket will deliver the error.
- Calling pru_accept directly races against the inpcb.inp_f{addr,port}
  setting up in the protocol thread, since inpcb.inp_f{addr,port} is
  setup _after_ the accepted socket was put onto the complete list.

     user thread                  proto thread
          :                             :
          :                  accepted socket -> comp
          :             (inpcb.inp_f{addr,port} are 0 here)
    comp -> socket                      :
     pru_accept                         :
          :                setup inpcb.inp_f{addr,port}

  Returning of 0.0.0.0:0 from accept(2) was observed on heavily loaded
  web servers.
sys/kern/uipc_socket.c
sys/kern/uipc_socket2.c
sys/kern/uipc_syscalls.c
sys/netinet/in_pcb.c
sys/netinet/in_pcb.h
sys/netinet/tcp_syncache.c
sys/netinet/tcp_usrreq.c
sys/sys/protosw.h
sys/sys/socketvar.h