[port] Count ephemeral packets from default arena

If a port packet is allocated from an arena that is not the default, it
should not be counted towards the default arena limit.

ZX-4220 #done

Change-Id: I46dc0f5eb683dfd80cc7affa8fa71a59a19372ea
diff --git a/zircon/kernel/object/port_dispatcher.cpp b/zircon/kernel/object/port_dispatcher.cpp
index 0c0bfef..f5c935e 100644
--- a/zircon/kernel/object/port_dispatcher.cpp
+++ b/zircon/kernel/object/port_dispatcher.cpp
@@ -58,6 +58,10 @@
 // TODO(maniscalco): Enforce this limit per process via the job policy.
 constexpr size_t kMaxAllocatedPacketCountPerPort = kMaxAllocatedPacketCount / 8;
 ArenaPortAllocator port_allocator;
+
+bool IsDefaultAllocatedEphemeral(const PortPacket& port_packet) {
+    return port_packet.allocator == &port_allocator && port_packet.is_ephemeral();
+}
 } // namespace.
 
 zx_status_t ArenaPortAllocator::Init() {
@@ -215,7 +219,7 @@
 
         // If the packet is ephemeral, free it outside of the lock. Otherwise,
         // reset the observer if it is present.
-        if (packet->is_ephemeral()) {
+        if (IsDefaultAllocatedEphemeral(*packet)) {
             --num_ephemeral_packets_;
             guard.CallUnlocked([packet]() {
                     packet->Free();
@@ -300,7 +304,8 @@
     if (zero_handles_)
         return ZX_ERR_BAD_STATE;
 
-    if (port_packet->is_ephemeral() && num_ephemeral_packets_ > kMaxAllocatedPacketCountPerPort) {
+    if (IsDefaultAllocatedEphemeral(*port_packet) &&
+        num_ephemeral_packets_ > kMaxAllocatedPacketCountPerPort) {
         kcounter_add(port_full_count, 1);
         return ZX_ERR_SHOULD_WAIT;
     }
@@ -315,7 +320,7 @@
         port_packet->packet.signal.count = count;
     }
     packets_.push_back(port_packet);
-    if (port_packet->is_ephemeral()) {
+    if (IsDefaultAllocatedEphemeral(*port_packet)) {
         ++num_ephemeral_packets_;
     }
     // This Disable() call must come before Post() to be useful, but doing
@@ -347,7 +352,7 @@
             Guard<fbl::Mutex> guard{get_lock()};
             PortPacket* port_packet = packets_.pop_front();
             if (port_packet != nullptr) {
-                if (port_packet->is_ephemeral()) {
+                if (IsDefaultAllocatedEphemeral(*port_packet)) {
                     --num_ephemeral_packets_;
                 }
                 *out_packet = port_packet->packet;
@@ -478,7 +483,7 @@
     for (auto it = packets_.begin(); it != packets_.end();) {
         if ((it->handle == handle) && (it->key() == key)) {
             auto to_remove = it++;
-            if (to_remove->is_ephemeral()) {
+            if (IsDefaultAllocatedEphemeral(*to_remove)) {
                 --num_ephemeral_packets_;
             }
             // Destroyed as we go around the loop.
@@ -499,7 +504,7 @@
     Guard<fbl::Mutex> guard{get_lock()};
 
     if (port_packet->InContainer()) {
-        if (port_packet->is_ephemeral()) {
+        if (IsDefaultAllocatedEphemeral(*port_packet)) {
             --num_ephemeral_packets_;
         }
         packets_.erase(*port_packet)->observer.reset();
diff --git a/zircon/system/utest/hypervisor/guest.cpp b/zircon/system/utest/hypervisor/guest.cpp
index f75c2d7..6886e30 100644
--- a/zircon/system/utest/hypervisor/guest.cpp
+++ b/zircon/system/utest/hypervisor/guest.cpp
@@ -521,6 +521,37 @@
     END_TEST;
 }
 
+// Test for ZX-4220.
+static bool guest_set_trap_with_bell_and_max_user() {
+    BEGIN_TEST;
+
+    zx::port port;
+    ASSERT_EQ(zx::port::create(0, &port), ZX_OK);
+
+    // Keep queueing packets until we receive ZX_ERR_SHOULD_WAIT.
+    zx_port_packet packet = {};
+    packet.type = ZX_PKT_TYPE_USER;
+    zx_status_t status = ZX_OK;
+    while ((status = port.queue(&packet)) == ZX_OK) {}
+    ASSERT_EQ(status, ZX_ERR_SHOULD_WAIT);
+
+    test_t test;
+    ASSERT_TRUE(setup(&test, guest_set_trap_start, guest_set_trap_end));
+    if (!test.supported) {
+        // The hypervisor isn't supported, so don't run the test.
+        return true;
+    }
+
+    // Trap on access of TRAP_ADDR.
+    ASSERT_EQ(test.guest.set_trap(ZX_GUEST_TRAP_BELL, TRAP_ADDR, PAGE_SIZE, port, kTrapKey), ZX_OK);
+
+    ASSERT_EQ(test.vcpu.resume(&packet), ZX_OK);
+    EXPECT_EQ(packet.type, ZX_PKT_TYPE_GUEST_MEM);
+    EXPECT_EQ(packet.guest_mem.addr, EXIT_TEST_ADDR);
+
+    END_TEST;
+}
+
 #ifdef __aarch64__
 
 static bool vcpu_wfi() {
@@ -941,6 +972,7 @@
 RUN_TEST(guest_set_trap_with_bell)
 RUN_TEST(guest_set_trap_with_bell_drop)
 RUN_TEST(guest_set_trap_with_bell_and_user)
+RUN_TEST(guest_set_trap_with_bell_and_max_user)
 #if __aarch64__
 RUN_TEST(vcpu_wfi)
 RUN_TEST(vcpu_wfi_pending_interrupt)