| // Copyright 2016 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #ifndef ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_DISPATCHER_H_ |
| #define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_DISPATCHER_H_ |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include <zircon/compiler.h> |
| #include <zircon/syscalls/object.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <fbl/auto_lock.h> |
| #include <fbl/canary.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/intrusive_single_list.h> |
| #include <fbl/recycler.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_counted_upgradeable.h> |
| #include <fbl/ref_ptr.h> |
| #include <kernel/lockdep.h> |
| #include <kernel/mutex.h> |
| #include <kernel/spinlock.h> |
| #include <ktl/move.h> |
| #include <ktl/type_traits.h> |
| #include <ktl/unique_ptr.h> |
| #include <object/handle.h> |
| #include <object/signal_observer.h> |
| |
| template <typename T> |
| struct DispatchTag; |
| template <typename T> |
| struct CanaryTag; |
| |
| #define DECLARE_DISPTAG(T, E, M) \ |
| class T; \ |
| template <> \ |
| struct DispatchTag<T> { \ |
| static constexpr zx_obj_type_t ID = E; \ |
| }; \ |
| template <> \ |
| struct CanaryTag<T> { \ |
| static constexpr uint32_t magic = fbl::magic(M); \ |
| }; |
| |
| DECLARE_DISPTAG(ProcessDispatcher, ZX_OBJ_TYPE_PROCESS, "PROC") |
| DECLARE_DISPTAG(ThreadDispatcher, ZX_OBJ_TYPE_THREAD, "THRD") |
| DECLARE_DISPTAG(VmObjectDispatcher, ZX_OBJ_TYPE_VMO, "VMOD") |
| DECLARE_DISPTAG(ChannelDispatcher, ZX_OBJ_TYPE_CHANNEL, "CHAN") |
| DECLARE_DISPTAG(EventDispatcher, ZX_OBJ_TYPE_EVENT, "EVTD") |
| DECLARE_DISPTAG(PortDispatcher, ZX_OBJ_TYPE_PORT, "PORT") |
| DECLARE_DISPTAG(InterruptDispatcher, ZX_OBJ_TYPE_INTERRUPT, "INTD") |
| DECLARE_DISPTAG(PciDeviceDispatcher, ZX_OBJ_TYPE_PCI_DEVICE, "PCID") |
| DECLARE_DISPTAG(LogDispatcher, ZX_OBJ_TYPE_LOG, "LOGD") |
| DECLARE_DISPTAG(SocketDispatcher, ZX_OBJ_TYPE_SOCKET, "SOCK") |
| DECLARE_DISPTAG(ResourceDispatcher, ZX_OBJ_TYPE_RESOURCE, "RSRD") |
| DECLARE_DISPTAG(EventPairDispatcher, ZX_OBJ_TYPE_EVENTPAIR, "EPAI") |
| DECLARE_DISPTAG(JobDispatcher, ZX_OBJ_TYPE_JOB, "JOBD") |
| DECLARE_DISPTAG(VmAddressRegionDispatcher, ZX_OBJ_TYPE_VMAR, "VARD") |
| DECLARE_DISPTAG(FifoDispatcher, ZX_OBJ_TYPE_FIFO, "FIFO") |
| DECLARE_DISPTAG(GuestDispatcher, ZX_OBJ_TYPE_GUEST, "GSTD") |
| DECLARE_DISPTAG(VcpuDispatcher, ZX_OBJ_TYPE_VCPU, "VCPU") |
| DECLARE_DISPTAG(TimerDispatcher, ZX_OBJ_TYPE_TIMER, "TIMR") |
| DECLARE_DISPTAG(IommuDispatcher, ZX_OBJ_TYPE_IOMMU, "IOMM") |
| DECLARE_DISPTAG(BusTransactionInitiatorDispatcher, ZX_OBJ_TYPE_BTI, "BTID") |
| DECLARE_DISPTAG(ProfileDispatcher, ZX_OBJ_TYPE_PROFILE, "PROF") |
| DECLARE_DISPTAG(PinnedMemoryTokenDispatcher, ZX_OBJ_TYPE_PMT, "PIMT") |
| DECLARE_DISPTAG(SuspendTokenDispatcher, ZX_OBJ_TYPE_SUSPEND_TOKEN, "SUTD") |
| DECLARE_DISPTAG(PagerDispatcher, ZX_OBJ_TYPE_PAGER, "PGRD") |
| DECLARE_DISPTAG(ExceptionDispatcher, ZX_OBJ_TYPE_EXCEPTION, "EXCD") |
| DECLARE_DISPTAG(ClockDispatcher, ZX_OBJ_TYPE_CLOCK, "CLOK") |
| DECLARE_DISPTAG(StreamDispatcher, ZX_OBJ_TYPE_STREAM, "STRM") |
| DECLARE_DISPTAG(MsiAllocationDispatcher, ZX_OBJ_TYPE_MSI_ALLOCATION, "MSIA") |
| DECLARE_DISPTAG(MsiDispatcher, ZX_OBJ_TYPE_MSI_INTERRUPT, "MSII") |
| |
| #undef DECLARE_DISPTAG |
| |
| // Base class for all kernel objects that can be exposed to user-mode via |
| // the syscall API and referenced by handles. |
| // |
| // It implements RefCounted because handles are abstractions to a multiple |
| // references from user mode or kernel mode that control the lifetime of |
| // the object. |
| // |
| // It implements Recyclable because upon final Release() on the RefPtr |
| // it might be necessary to implement a destruction pattern that avoids |
| // deep recursion since the kernel stack is very limited. |
| // |
| // You don't derive directly from this class; instead derive |
| // from SoloDispatcher or PeeredDispatcher. |
| class Dispatcher : private fbl::RefCountedUpgradeable<Dispatcher>, |
| private fbl::Recyclable<Dispatcher> { |
| public: |
| using fbl::RefCountedUpgradeable<Dispatcher>::AddRef; |
| using fbl::RefCountedUpgradeable<Dispatcher>::Release; |
| using fbl::RefCountedUpgradeable<Dispatcher>::Adopt; |
| using fbl::RefCountedUpgradeable<Dispatcher>::AddRefMaybeInDestructor; |
| |
| // Dispatchers are either Solo or Peered. They handle refcounting |
| // and locking differently. |
| virtual ~Dispatcher(); |
| |
| zx_koid_t get_koid() const { return koid_; } |
| |
| void increment_handle_count() { |
| // As this function does not return anything actionable, not even something implicit like "you |
| // now have the lock", there are no correct assumptions the caller can make about orderings |
| // of this increment and any other memory access. As such it can just be relaxed. |
| handle_count_.fetch_add(1, ktl::memory_order_relaxed); |
| } |
| |
| // Returns true exactly when the handle count goes to zero. |
| bool decrement_handle_count() { |
| if (handle_count_.fetch_sub(1, ktl::memory_order_release) == 1u) { |
| // The decrement operation above synchronizes with the fence below. This ensures that changes |
| // to the object prior to its handle count reaching 0 will be visible to the thread that |
| // ultimately drops the count to 0. This is similar to what's done in |
| // |fbl::RefCountedInternal|. |
| ktl::atomic_thread_fence(ktl::memory_order_acquire); |
| return true; |
| } |
| return false; |
| } |
| |
| uint32_t current_handle_count() const { |
| // Requesting the count is fundamentally racy with other users of the dispatcher. A typical |
| // reference count implementation might place an acquire here for the scenario where you then |
| // run an object destructor without acquiring any locks. As a handle count is not a refcount |
| // and a low handle count does not imply any ownership of the dispatcher (which has its own |
| // refcount), this can just be relaxed. |
| return handle_count_.load(ktl::memory_order_relaxed); |
| } |
| |
| enum class TriggerMode : uint32_t { |
| Level = 0, |
| Edge, |
| }; |
| |
| // Add a observer which will be triggered when any |signal| becomes active |
| // or cancelled when |handle| is destroyed. |
| // |
| // |observer| must be non-null, and |is_waitable| must report true. |
| // |
| // Be sure to |RemoveObserver| before the Dispatcher is destroyed. |
| // |
| // If |trigger_mode| is set to Edge, the signal state is not checked |
| // on entry and the observer is only triggered if a signal subsequently |
| // becomes active. |
| zx_status_t AddObserver(SignalObserver* observer, const Handle* handle, zx_signals_t signals, |
| TriggerMode trigger_mode = TriggerMode::Level); |
| |
| // Remove an observer. |
| // |
| // Returns true if the method removed |observer|, otherwise returns false. If |
| // provided, |signals| will be given the current state of the dispatcher's |
| // signals when the observer was removed. |
| // |
| // This method may return false if the observer was never added or has already been removed in |
| // preparation for its destruction. |
| // |
| // It is an error to call this method with an observer that's observing some other Dispatcher. |
| // |
| // May only be called when |is_waitable| reports true. |
| bool RemoveObserver(SignalObserver* observer, zx_signals_t* signals = nullptr); |
| |
| // Cancel observers of this object's state (e.g., waits on the object). |
| // Should be called when a handle to this dispatcher is being destroyed. |
| // |
| // May only be called when |is_waitable| reports true. |
| void Cancel(const Handle* handle); |
| |
| // Like Cancel() but issued via via zx_port_cancel(). |
| // |
| // Returns true if an observer was canceled. |
| // |
| // May only be called when |is_waitable| reports true. |
| bool CancelByKey(const Handle* handle, const void* port, uint64_t key); |
| |
| // Interface for derived classes. |
| |
| virtual zx_obj_type_t get_type() const = 0; |
| |
| virtual zx_status_t user_signal_self(uint32_t clear_mask, uint32_t set_mask) = 0; |
| virtual zx_status_t user_signal_peer(uint32_t clear_mask, uint32_t set_mask) = 0; |
| |
| virtual void on_zero_handles() {} |
| |
| virtual zx_koid_t get_related_koid() const = 0; |
| virtual bool is_waitable() const = 0; |
| |
| // get_name() will return a null-terminated name of ZX_MAX_NAME_LEN - 1 or fewer |
| // characters. For objects that don't have names it will be "". |
| virtual void get_name(char out_name[ZX_MAX_NAME_LEN]) const __NONNULL((2)) { |
| memset(out_name, 0, ZX_MAX_NAME_LEN); |
| } |
| |
| // set_name() will truncate to ZX_MAX_NAME_LEN - 1 and ensure there is a |
| // terminating null |
| virtual zx_status_t set_name(const char* name, size_t len) { return ZX_ERR_NOT_SUPPORTED; } |
| |
| struct DeleterListTraits { |
| static fbl::SinglyLinkedListNodeState<Dispatcher*>& node_state(Dispatcher& obj) { |
| return obj.deleter_ll_; |
| } |
| }; |
| |
| // Called whenever the object is bound to a new process. The |new_owner| is |
| // the koid of the new process. It is only overridden for objects where a single |
| // owner makes sense. |
| virtual void set_owner(zx_koid_t new_owner) {} |
| |
| // Poll the currently active signals on this object. |
| // |
| // By the time the result of the function is inspected, the signals may have already |
| // changed. Typically should only be used for tests or logging. |
| zx_signals_t PollSignals() const TA_EXCL(get_lock()); |
| |
| protected: |
| // At construction, the object is asserting |signals|. |
| explicit Dispatcher(zx_signals_t signals); |
| |
| // Notify others of a change in signals (possibly waking them). (Clearing satisfied signals or |
| // setting satisfiable signals should not wake anyone.) |
| // |
| // May only be called when |is_waitable| reports true. |
| void UpdateState(zx_signals_t clear_mask, zx_signals_t set_mask) TA_EXCL(get_lock()); |
| void UpdateStateLocked(zx_signals_t clear_mask, zx_signals_t set_mask) TA_REQ(get_lock()); |
| |
| zx_signals_t GetSignalsStateLocked() const TA_REQ(get_lock()) { return signals_; } |
| |
| // Dispatcher subtypes should use this lock to protect their internal state. |
| virtual Lock<Mutex>* get_lock() const = 0; |
| |
| private: |
| friend class fbl::Recyclable<Dispatcher>; |
| void fbl_recycle(); |
| |
| fbl::Canary<fbl::magic("DISP")> canary_; |
| |
| const zx_koid_t koid_; |
| ktl::atomic<uint32_t> handle_count_; |
| |
| zx_signals_t signals_ TA_GUARDED(get_lock()); |
| |
| // List of observers watching for changes in signals on this dispatcher. |
| fbl::DoublyLinkedList<SignalObserver*> observers_ TA_GUARDED(get_lock()); |
| |
| // Used to store this dispatcher on the dispatcher deleter list. |
| fbl::SinglyLinkedListNodeState<Dispatcher*> deleter_ll_; |
| }; |
| |
| // SoloDispatchers stand alone. Since they have no peer to coordinate with, they |
| // directly contain their state lock. This is a CRTP template type to permit |
| // the lock validator to distinguish between locks in different subclasses of |
| // SoloDispatcher. |
| template <typename Self, zx_rights_t def_rights, zx_signals_t extra_signals = 0u, |
| lockdep::LockFlags Flags = lockdep::LockFlagsNone> |
| class SoloDispatcher : public Dispatcher { |
| public: |
| static constexpr zx_rights_t default_rights() { return def_rights; } |
| |
| // At construction, the object is asserting |signals|. |
| explicit SoloDispatcher(zx_signals_t signals = 0u) : Dispatcher(signals) {} |
| |
| // Related koid is overridden by subclasses, like thread and process. |
| zx_koid_t get_related_koid() const override TA_REQ(get_lock()) { return 0ULL; } |
| bool is_waitable() const final { return default_rights() & ZX_RIGHT_WAIT; } |
| |
| zx_status_t user_signal_self(uint32_t clear_mask, uint32_t set_mask) final { |
| if (!is_waitable()) |
| return ZX_ERR_NOT_SUPPORTED; |
| // Generic objects can set all USER_SIGNALs. Particular object |
| // types (events and eventpairs) may be able to set more. |
| auto allowed_signals = ZX_USER_SIGNAL_ALL | extra_signals; |
| if ((set_mask & ~allowed_signals) || (clear_mask & ~allowed_signals)) |
| return ZX_ERR_INVALID_ARGS; |
| |
| UpdateState(clear_mask, set_mask); |
| return ZX_OK; |
| } |
| |
| zx_status_t user_signal_peer(uint32_t clear_mask, uint32_t set_mask) final { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| protected: |
| Lock<Mutex>* get_lock() const final { return &lock_; } |
| |
| const fbl::Canary<CanaryTag<Self>::magic> canary_; |
| mutable DECLARE_MUTEX(SoloDispatcher, Flags) lock_; |
| }; |
| |
| // PeeredDispatchers have opposing endpoints to coordinate state |
| // with. For example, writing into one endpoint of a Channel needs to |
| // modify zx_signals_t state (for the readability bit) on the opposite |
| // side. To coordinate their state, they share a mutex, which is held |
| // by the PeerHolder. Both endpoints have a RefPtr back to the |
| // PeerHolder; no one else ever does. |
| |
| // Thus creating a pair of peered objects will typically look |
| // something like |
| // // Make the two RefPtrs for each endpoint's handle to the mutex. |
| // auto holder0 = AdoptRef(new PeerHolder<Foo>(...)); |
| // auto holder1 = peer_holder0; |
| // // Create the opposing sides. |
| // auto foo0 = AdoptRef(new Foo(ktl::move(holder0, ...)); |
| // auto foo1 = AdoptRef(new Foo(ktl::move(holder1, ...)); |
| // // Initialize the opposing sides, teaching them about each other. |
| // foo0->Init(&foo1); |
| // foo1->Init(&foo0); |
| |
| // A PeeredDispatcher object, in its |on_zero_handles| call must clear |
| // out its peer's |peer_| field. This is needed to avoid leaks, and to |
| // ensure that |user_signal| can correctly report ZX_ERR_PEER_CLOSED. |
| |
| // TODO(kulakowski) We should investigate turning this into one |
| // allocation. This would mean PeerHolder would have two EndPoint |
| // members, and that PeeredDispatcher would have custom refcounting. |
| template <typename Endpoint> |
| class PeerHolder : public fbl::RefCounted<PeerHolder<Endpoint>> { |
| public: |
| PeerHolder() = default; |
| ~PeerHolder() = default; |
| |
| Lock<Mutex>* get_lock() const { return &lock_; } |
| |
| mutable DECLARE_MUTEX(PeerHolder) lock_; |
| }; |
| |
| template <typename Self, zx_rights_t def_rights, zx_signals_t extra_signals = 0u> |
| class PeeredDispatcher : public Dispatcher { |
| public: |
| static constexpr zx_rights_t default_rights() { return def_rights; } |
| |
| // At construction, the object is asserting |signals|. |
| explicit PeeredDispatcher(fbl::RefPtr<PeerHolder<Self>> holder, zx_signals_t signals = 0u) |
| : Dispatcher(signals), holder_(ktl::move(holder)) {} |
| virtual ~PeeredDispatcher() = default; |
| |
| zx_koid_t get_related_koid() const final TA_REQ(get_lock()) { return peer_koid_; } |
| bool is_waitable() const final { return default_rights() & ZX_RIGHT_WAIT; } |
| |
| zx_status_t user_signal_self(uint32_t clear_mask, |
| uint32_t set_mask) final TA_NO_THREAD_SAFETY_ANALYSIS { |
| auto allowed_signals = ZX_USER_SIGNAL_ALL | extra_signals; |
| if ((set_mask & ~allowed_signals) || (clear_mask & ~allowed_signals)) |
| return ZX_ERR_INVALID_ARGS; |
| |
| Guard<Mutex> guard{get_lock()}; |
| |
| UpdateStateLocked(clear_mask, set_mask); |
| return ZX_OK; |
| } |
| |
| zx_status_t user_signal_peer(uint32_t clear_mask, |
| uint32_t set_mask) final TA_NO_THREAD_SAFETY_ANALYSIS { |
| auto allowed_signals = ZX_USER_SIGNAL_ALL | extra_signals; |
| if ((set_mask & ~allowed_signals) || (clear_mask & ~allowed_signals)) |
| return ZX_ERR_INVALID_ARGS; |
| |
| Guard<Mutex> guard{get_lock()}; |
| // object_signal() may race with handle_close() on another thread. |
| if (!peer_) |
| return ZX_ERR_PEER_CLOSED; |
| peer_->UpdateStateLocked(clear_mask, set_mask); |
| return ZX_OK; |
| } |
| |
| // All subclasses of PeeredDispatcher must implement a public |
| // |void on_zero_handles_locked()|. The peer lifetime management |
| // (i.e. the peer zeroing) is centralized here. |
| void on_zero_handles() final TA_NO_THREAD_SAFETY_ANALYSIS { |
| Guard<Mutex> guard{get_lock()}; |
| auto peer = ktl::move(peer_); |
| static_cast<Self*>(this)->on_zero_handles_locked(); |
| |
| // This is needed to avoid leaks, and to ensure that |
| // |user_signal| can correctly report ZX_ERR_PEER_CLOSED. |
| if (peer != nullptr) { |
| // This defeats the lock analysis in the usual way: it |
| // can't reason that the peers' get_lock() calls alias. |
| peer->peer_.reset(); |
| static_cast<Self*>(peer.get())->OnPeerZeroHandlesLocked(); |
| } |
| } |
| |
| // Returns true if the peer has closed. Once the peer has closed it |
| // will never re-open. |
| bool PeerHasClosed() const { |
| Guard<Mutex> guard{get_lock()}; |
| return peer_ == nullptr; |
| } |
| |
| Lock<Mutex>* get_lock() const final { return holder_->get_lock(); } |
| |
| protected: |
| const fbl::Canary<CanaryTag<Self>::magic> canary_; |
| |
| zx_koid_t peer_koid_ = 0u; |
| fbl::RefPtr<Self> peer_ TA_GUARDED(get_lock()); |
| |
| private: |
| const fbl::RefPtr<PeerHolder<Self>> holder_; |
| }; |
| |
| // DownCastDispatcher checks if a RefPtr<Dispatcher> points to a |
| // dispatcher of a given dispatcher subclass T and, if so, moves the |
| // reference to a RefPtr<T>, otherwise it leaves the |
| // RefPtr<Dispatcher> alone. Must be called with a pointer to a valid |
| // (non-null) dispatcher. |
| |
| // Note that the Dispatcher -> Dispatcher versions come up in generic |
| // code, and so aren't totally vacuous. |
| |
| // Dispatcher -> FooDispatcher |
| template <typename T> |
| fbl::RefPtr<T> DownCastDispatcher(fbl::RefPtr<Dispatcher>* disp) { |
| return (likely(DispatchTag<T>::ID == (*disp)->get_type())) |
| ? fbl::RefPtr<T>::Downcast(ktl::move(*disp)) |
| : nullptr; |
| } |
| |
| // Dispatcher -> Dispatcher |
| template <> |
| inline fbl::RefPtr<Dispatcher> DownCastDispatcher(fbl::RefPtr<Dispatcher>* disp) { |
| return ktl::move(*disp); |
| } |
| |
| // const Dispatcher -> const FooDispatcher |
| template <typename T> |
| fbl::RefPtr<T> DownCastDispatcher(fbl::RefPtr<const Dispatcher>* disp) { |
| static_assert(ktl::is_const<T>::value, ""); |
| return (likely(DispatchTag<typename ktl::remove_const<T>::type>::ID == (*disp)->get_type())) |
| ? fbl::RefPtr<T>::Downcast(ktl::move(*disp)) |
| : nullptr; |
| } |
| |
| // const Dispatcher -> const Dispatcher |
| template <> |
| inline fbl::RefPtr<const Dispatcher> DownCastDispatcher(fbl::RefPtr<const Dispatcher>* disp) { |
| return ktl::move(*disp); |
| } |
| |
| // The same, but for Dispatcher* and FooDispatcher* instead of RefPtr. |
| |
| // Dispatcher -> FooDispatcher |
| template <typename T> |
| T* DownCastDispatcher(Dispatcher* disp) { |
| return (likely(DispatchTag<T>::ID == disp->get_type())) ? reinterpret_cast<T*>(disp) : nullptr; |
| } |
| |
| // Dispatcher -> Dispatcher |
| template <> |
| inline Dispatcher* DownCastDispatcher(Dispatcher* disp) { |
| return disp; |
| } |
| |
| // const Dispatcher -> const FooDispatcher |
| template <typename T> |
| const T* DownCastDispatcher(const Dispatcher* disp) { |
| static_assert(ktl::is_const<T>::value, ""); |
| return (likely(DispatchTag<typename ktl::remove_const<T>::type>::ID == disp->get_type())) |
| ? reinterpret_cast<const T*>(disp) |
| : nullptr; |
| } |
| |
| // const Dispatcher -> const Dispatcher |
| template <> |
| inline const Dispatcher* DownCastDispatcher(const Dispatcher* disp) { |
| return disp; |
| } |
| |
| #endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_DISPATCHER_H_ |