blob: 6b6da303957dc065dfb395046de0b6324212c15b [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors
// Copyright (c) 2019, Google, Inc. All rights reserved
//
// 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
#pragma once
#include <assert.h>
#include <fbl/ref_counted.h>
#include <zircon/compiler.h>
/**
* Notes on class hierarchy and RefCounting
*
* The PCI/PCIe device class hierarchy consists of 3 main types of object.
*
* ++ Root
* A root of a PCI/PCIe device tree. Roots do not have standard config
* registers, but do have a collection of downstream Device children. In
* addition, PCIe roots (as opposed to plain PCI roots) have a special set of
* registers called the "root complex control block". The PCIe bus driver
* supports systems which have multiple roots and maintains a collection of
* roots which were registered by the system.
*
* ++ Device
* The actual devices in the PCIe hierarchy. Devices have a set of PCI/PCIe
* config registers, can allocate apertures in Memory and I/O space, can map
* interrupts, and can have drivers attached to them. All devices are the child
* of either a Root or a Bridge, but have no children themselves.
*
* ++ Bridge
* Bridges are devices with children. Because they are devices, bridges
* have config, can map registers, deliver interrupts, have drivers bound to
* them, and are always the child of either a Root or another Bridge.
* In addition (unlike Devices), Bridges have roots.
*
* In order to avoid code duplication, two classes have been introduced and
* Bridge makes limited use of multiple inheritance in order to be a device
* with children, while not being a root. The classes introduced are...
*
* ++ UpstreamNode
* A UpstreamNode is an object which can have Device children. Roots
* and Bridges are both are upstream nodes. Devices hold a reference to their
* upstream node, without needing to understand whether they are downstream of a
* root or a bridge.
*
* ++ DeviceImpl
* A small class used to deal with some of the ref counting issues which arise
* from this arrangement. More on this later.
*
* A simple diagram of the class hierarchy looks like this.
*
* +---------------+ +--------+
* | Upstream Node | | Device |
* +---------------+ +--------+
* | | | |
* +------+ | | +--------+ | |
* | Root | <---/ \--->| Bridge |<---/ |
* +------+ +--------+ |
* |
* +------------+ |
* | DeviceImpl |<-------/
* +------------+
*
* ==== RefCounting ====
*
* Object lifetimes are managed using fbl::RefPtr<>. Because of this, all
* objects must provide an implementation of AddRef/Release/Adopt which is
* compatible with fbl::RefPtr<>. The bus driver holds RefPtr<Root>s,
* UpstreamNodes hold RefPtr<Device>s and Devices hold RefPtr<UpstreamNode>s
* back to their owners.
*
* RefPtr to both Devices and UpstreamNodes exist in the system, so both object
* must expose an interface to reference counting which is compatible with
* fbl::RefPtr<>. Because a Bridge is both an UpstreamNode and a Device,
* simply having Device and UpstreamNode derive from fbl::RefCounted<> (which
* would be standard practice) will not work. The Bridge object which results
* from this would have two different ref counts which would end up being
* manipulated independently.
*
* A simple solution to this would be to have all of the objects in the system
* inherit virtually from an implementation of fbl::RefCounted<>.
* Unfortunately, the power that be strictly prohibit the use of virtual
* inheritance in this codebase. Because of this, a different solution needs to
* be provided. Here is how this system works.
*
* Two macros have been defined (below). One or the other of them *must* be
* included in the public section of every class involved in this hierarchy.
*
* ++ PCI_REQUIRE_REFCOUNTED
* Any class which is a base class of any other class in this hierarchy *must*
* include this macro. It defines pure virtual AddRef/Release/Adopt members
* whose signatures are compatible with fbl::RefPtr. This requires an
* implementation to exist in derived classes, redirects ref counting behavior
* to this implementation, and prevents accidental instantiation of the base
* class. UpstreamNode and Device REQUIRE_REFCOUNTED.
*
* ++ PCI_IMPLEMENT_REFCOUNTED
* Any class which is a child of one or more of the base classes *must* include
* this macro. This macro wraps an implementation of fbl::RefCounted<> (so
* that code duplication is minimized, and atomic ref count access is consistent
* throughout the system), and marks the virtual AddRef/Release/Adopt methods as
* being final, which helps to prevent a different implementation accidentally
* being added to the class hierarchy. Root, Bridge and DeviceImpl
* IMPLEMENT_REFCOUNTED.
*
* Finally, coming back to the issue of DeviceImpl...
* Because Device is a base class for Bridge, it cannot IMPLEMENT_REFCOUNTED
* itself. Instead, it must REQUIRE_REFCOUNTED (redirecting ref counting
* behavior to the Bridge implementation). This means that Device can no longer
* be instantiated (because it is abstract). DeviceImpl is a small class which
* does nothing but derive from Device and implement the ref counting. Its
* implementation exists inside of an anonymous namespace inside
* pcie_device.cpp so none of the rest of the system ever sees it.
* Device::Create returns a fbl::RefPtr<Device> which actually points
* to an instance of DeviceImpl created by the Create method.
*/
#define PCI_REQUIRE_REFCOUNTED \
virtual void Adopt() = 0; \
virtual void AddRef() = 0; \
virtual bool Release() = 0
#define PCI_IMPLEMENT_REFCOUNTED \
private: \
fbl::RefCounted<void> ref_count_impl_; \
\
public: \
void Adopt() final { ref_count_impl_.Adopt(); } \
void AddRef() final { ref_count_impl_.AddRef(); } \
bool Release() final __WARN_UNUSED_RESULT { return ref_count_impl_.Release(); } \
using __force_semicolon = int