+/* Mechanism used to defer the memory release of PCBs
+ * The pcb list will contain the pcb until the ripper can clean it up if
+ * the following conditions are met: 1) state "DEAD", 2) wantcnt is STOPUSING
+ * 3) usecount is null
+ * This function will be called to either mark the pcb as
+*/
+int
+in_pcb_checkstate(struct inpcb *pcb, int mode, int locked)
+
+{
+
+ volatile UInt32 *wantcnt = (volatile UInt32 *)&pcb->inp_wantcnt;
+ UInt32 origwant;
+ UInt32 newwant;
+
+ switch (mode) {
+
+ case WNT_STOPUSING: /* try to mark the pcb as ready for recycling */
+
+ /* compareswap with STOPUSING, if success we're good, if it's in use, will be marked later */
+
+ if (locked == 0)
+ socket_lock(pcb->inp_socket, 1);
+ pcb->inp_state = INPCB_STATE_DEAD;
+stopusing:
+ if (pcb->inp_socket->so_usecount < 0)
+ panic("in_pcb_checkstate STOP pcb=%x so=%x usecount is negative\n", pcb, pcb->inp_socket);
+ if (locked == 0)
+ socket_unlock(pcb->inp_socket, 1);
+
+ origwant = *wantcnt;
+ if ((UInt16) origwant == 0xffff ) /* should stop using */
+ return (WNT_STOPUSING);
+ newwant = 0xffff;
+ if ((UInt16) origwant == 0) {/* try to mark it as unsuable now */
+ OSCompareAndSwap(origwant, newwant, (UInt32 *) wantcnt) ;
+ }
+ return (WNT_STOPUSING);
+ break;
+
+ case WNT_ACQUIRE: /* try to increase reference to pcb */
+ /* if WNT_STOPUSING should bail out */
+ /*
+ * if socket state DEAD, try to set count to STOPUSING, return failed
+ * otherwise increase cnt
+ */
+ do {
+ origwant = *wantcnt;
+ if ((UInt16) origwant == 0xffff ) {/* should stop using */
+// printf("in_pcb_checkstate: ACQ PCB was STOPUSING while release. odd pcb=%x\n", pcb);
+ return (WNT_STOPUSING);
+ }
+ newwant = origwant + 1;
+ } while (!OSCompareAndSwap(origwant, newwant, (UInt32 *) wantcnt));
+ return (WNT_ACQUIRE);
+ break;
+
+ case WNT_RELEASE: /* release reference. if result is null and pcb state is DEAD,
+ set wanted bit to STOPUSING
+ */
+
+ if (locked == 0)
+ socket_lock(pcb->inp_socket, 1);
+
+ do {
+ origwant = *wantcnt;
+ if ((UInt16) origwant == 0x0 )
+ panic("in_pcb_checkstate pcb=%x release with zero count", pcb);
+ if ((UInt16) origwant == 0xffff ) {/* should stop using */
+#if TEMPDEBUG
+ printf("in_pcb_checkstate: REL PCB was STOPUSING while release. odd pcb=%x\n", pcb);
+#endif
+ if (locked == 0)
+ socket_unlock(pcb->inp_socket, 1);
+ return (WNT_STOPUSING);
+ }
+ newwant = origwant - 1;
+ } while (!OSCompareAndSwap(origwant, newwant, (UInt32 *) wantcnt));
+
+ if (pcb->inp_state == INPCB_STATE_DEAD)
+ goto stopusing;
+ if (pcb->inp_socket->so_usecount < 0)
+ panic("in_pcb_checkstate RELEASE pcb=%x so=%x usecount is negative\n", pcb, pcb->inp_socket);
+
+ if (locked == 0)
+ socket_unlock(pcb->inp_socket, 1);
+ return (WNT_RELEASE);
+ break;
+
+ default:
+
+ panic("in_pcb_checkstate: so=%x not a valid state =%x\n", pcb->inp_socket, mode);
+ }
+
+ /* NOTREACHED */
+ return (mode);
+}
+
+/*
+ * inpcb_to_compat copies specific bits of an inpcb to a inpcb_compat.
+ * The inpcb_compat data structure is passed to user space and must
+ * not change. We intentionally avoid copying pointers. The socket is
+ * the one exception, though we probably shouldn't copy that either.
+ */
+void
+inpcb_to_compat(
+ struct inpcb *inp,
+ struct inpcb_compat *inp_compat)
+{
+ bzero(inp_compat, sizeof(*inp_compat));
+ inp_compat->inp_fport = inp->inp_fport;
+ inp_compat->inp_lport = inp->inp_lport;
+ inp_compat->inp_socket = inp->inp_socket;
+ inp_compat->nat_owner = inp->nat_owner;
+ inp_compat->nat_cookie = inp->nat_cookie;
+ inp_compat->inp_gencnt = inp->inp_gencnt;
+ inp_compat->inp_flags = inp->inp_flags;
+ inp_compat->inp_flow = inp->inp_flow;
+ inp_compat->inp_vflag = inp->inp_vflag;
+ inp_compat->inp_ip_ttl = inp->inp_ip_ttl;
+ inp_compat->inp_ip_p = inp->inp_ip_p;
+ inp_compat->inp_dependfaddr.inp6_foreign = inp->inp_dependfaddr.inp6_foreign;
+ inp_compat->inp_dependladdr.inp6_local = inp->inp_dependladdr.inp6_local;
+ inp_compat->inp_depend4.inp4_ip_tos = inp->inp_depend4.inp4_ip_tos;
+ inp_compat->inp_depend6.inp6_hlim = inp->inp_depend6.inp6_hlim;
+ inp_compat->inp_depend6.inp6_cksum = inp->inp_depend6.inp6_cksum;
+ inp_compat->inp6_ifindex = inp->inp6_ifindex;
+ inp_compat->inp_depend6.inp6_hops = inp->inp_depend6.inp6_hops;
+}