Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/dp_mst: Introduce new refcounting scheme for mstbs and ports

The current way of handling refcounting in the DP MST helpers is really
confusing and probably just plain wrong because it's been hacked up many
times over the years without anyone actually going over the code and
seeing if things could be simplified.

To the best of my understanding, the current scheme works like this:
drm_dp_mst_port and drm_dp_mst_branch both have a single refcount. When
this refcount hits 0 for either of the two, they're removed from the
topology state, but not immediately freed. Both ports and branch devices
will reinitialize their kref once it's hit 0 before actually destroying
themselves. The intended purpose behind this is so that we can avoid
problems like not being able to free a remote payload that might still
be active, due to us having removed all of the port/branch device
structures in memory, as per:

commit 91a25e463130 ("drm/dp/mst: deallocate payload on port destruction")

Which may have worked, but then it caused use-after-free errors. Being
new to MST at the time, I tried fixing it;

commit 263efde31f97 ("drm/dp/mst: Get validated port ref in drm_dp_update_payload_part1()")

But, that was broken: both drm_dp_mst_port and drm_dp_mst_branch structs
are validated in almost every DP MST helper function. Simply put, this
means we go through the topology and try to see if the given
drm_dp_mst_branch or drm_dp_mst_port is still attached to something
before trying to use it in order to avoid dereferencing freed memory
(something that has happened a LOT in the past with this library).
Because of this it doesn't actually matter whether or not we keep keep
the ports and branches around in memory as that's not enough, because
any function that validates the branches and ports passed to it will
still reject them anyway since they're no longer in the topology
structure. So, use-after-free errors were fixed but payload deallocation
was completely broken.

Two years later, AMD informed me about this issue and I attempted to
come up with a temporary fix, pending a long-overdue cleanup of this
library:

commit c54c7374ff44 ("drm/dp_mst: Skip validating ports during destruction, just ref")

But then that introduced use-after-free errors, so I quickly reverted
it:

commit 9765635b3075 ("Revert "drm/dp_mst: Skip validating ports during destruction, just ref"")

And in the process, learned that there is just no simple fix for this:
the design is just broken. Unfortunately, the usage of these helpers are
quite broken as well. Some drivers like i915 have been smart enough to
avoid accessing any kind of information from MST port structures, but
others like nouveau have assumed, understandably so, that
drm_dp_mst_port structures are normal and can just be accessed at any
time without worrying about use-after-free errors.

After a lot of discussion, me and Daniel Vetter came up with a better
idea to replace all of this.

To summarize, since this is documented far more indepth in the
documentation this patch introduces, we make it so that drm_dp_mst_port
and drm_dp_mst_branch structures have two different classes of
refcounts: topology_kref, and malloc_kref. topology_kref corresponds to
the lifetime of the given drm_dp_mst_port or drm_dp_mst_branch in it's
given topology. Once it hits zero, any associated connectors are removed
and the branch or port can no longer be validated. malloc_kref
corresponds to the lifetime of the memory allocation for the actual
structure, and will always be non-zero so long as the topology_kref is
non-zero. This gives us a way to allow callers to hold onto port and
branch device structures past their topology lifetime, and dramatically
simplifies the lifetimes of both structures. This also finally fixes the
port deallocation problem, properly.

Additionally: since this now means that we can keep ports and branch
devices allocated in memory for however long we need, we no longer need
a significant amount of the port validation that we currently do.

Additionally, there is one last scenario that this fixes, which couldn't
have been fixed properly beforehand:

- CPU1 unrefs port from topology (refcount 1->0)
- CPU2 refs port in topology(refcount 0->1)

Since we now can guarantee memory safety for ports and branches
as-needed, we also can make our main reference counting functions fix
this problem by using kref_get_unless_zero() internally so that topology
refcounts can only ever reach 0 once.

Changes since v4:
* Change the kernel-figure summary for dp-mst/topology-figure-1.dot a
bit - danvet
* Remove figure numbers - danvet

Changes since v3:
* Remove rebase detritus - danvet
* Split out purely style changes into separate patches - hwentlan

Changes since v2:
* Fix commit message - checkpatch
* s/)-1/) - 1/g - checkpatch

Changes since v1:
* Remove forward declarations - danvet
* Move "Branch device and port refcounting" section from documentation
into kernel-doc comments - danvet
* Export internal topology lifetime functions into their own section in
the kernel-docs - danvet
* s/@/&/g for struct references in kernel-docs - danvet
* Drop the "when they are no longer being used" bits from the kernel
docs - danvet
* Modify diagrams to show how the DRM driver interacts with the topology
and payloads - danvet
* Make suggested documentation changes for
drm_dp_mst_topology_get_mstb() and drm_dp_mst_topology_get_port() -
danvet
* Better explain the relationship between malloc refs and topology krefs
in the documentation for drm_dp_mst_topology_get_port() and
drm_dp_mst_topology_get_mstb() - danvet
* Fix "See also" in drm_dp_mst_topology_get_mstb() - danvet
* Rename drm_dp_mst_topology_get_(port|mstb)() ->
drm_dp_mst_topology_try_get_(port|mstb)() and
drm_dp_mst_topology_ref_(port|mstb)() ->
drm_dp_mst_topology_get_(port|mstb)() - danvet
* s/should/must in docs - danvet
* WARN_ON(refcount == 0) in topology_get_(mstb|port) - danvet
* Move kdocs for mstb/port structs inline - danvet
* Split drm_dp_get_last_connected_port_and_mstb() changes into their own
commit - danvet

Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Daniel Vetter <daniel@ffwll.ch>
Cc: David Airlie <airlied@redhat.com>
Cc: Jerry Zuo <Jerry.Zuo@amd.com>
Cc: Juston Li <juston.li@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190111005343.17443-7-lyude@redhat.com

+614 -69
+52
Documentation/gpu/dp-mst/topology-figure-1.dot
··· 1 + digraph T { 2 + /* Make sure our payloads are always drawn below the driver node */ 3 + subgraph cluster_driver { 4 + fillcolor = grey; 5 + style = filled; 6 + driver -> {payload1, payload2} [dir=none]; 7 + } 8 + 9 + /* Driver malloc references */ 10 + edge [style=dashed]; 11 + driver -> port1; 12 + driver -> port2; 13 + driver -> port3:e; 14 + driver -> port4; 15 + 16 + payload1:s -> port1:e; 17 + payload2:s -> port3:e; 18 + edge [style=""]; 19 + 20 + subgraph cluster_topology { 21 + label="Topology Manager"; 22 + labelloc=bottom; 23 + 24 + /* Topology references */ 25 + mstb1 -> {port1, port2}; 26 + port1 -> mstb2; 27 + port2 -> mstb3 -> {port3, port4}; 28 + port3 -> mstb4; 29 + 30 + /* Malloc references */ 31 + edge [style=dashed;dir=back]; 32 + mstb1 -> {port1, port2}; 33 + port1 -> mstb2; 34 + port2 -> mstb3 -> {port3, port4}; 35 + port3 -> mstb4; 36 + } 37 + 38 + driver [label="DRM driver";style=filled;shape=box;fillcolor=lightblue]; 39 + 40 + payload1 [label="Payload #1";style=filled;shape=box;fillcolor=lightblue]; 41 + payload2 [label="Payload #2";style=filled;shape=box;fillcolor=lightblue]; 42 + 43 + mstb1 [label="MSTB #1";style=filled;fillcolor=palegreen;shape=oval]; 44 + mstb2 [label="MSTB #2";style=filled;fillcolor=palegreen;shape=oval]; 45 + mstb3 [label="MSTB #3";style=filled;fillcolor=palegreen;shape=oval]; 46 + mstb4 [label="MSTB #4";style=filled;fillcolor=palegreen;shape=oval]; 47 + 48 + port1 [label="Port #1";shape=oval]; 49 + port2 [label="Port #2";shape=oval]; 50 + port3 [label="Port #3";shape=oval]; 51 + port4 [label="Port #4";shape=oval]; 52 + }
+56
Documentation/gpu/dp-mst/topology-figure-2.dot
··· 1 + digraph T { 2 + /* Make sure our payloads are always drawn below the driver node */ 3 + subgraph cluster_driver { 4 + fillcolor = grey; 5 + style = filled; 6 + driver -> {payload1, payload2} [dir=none]; 7 + } 8 + 9 + /* Driver malloc references */ 10 + edge [style=dashed]; 11 + driver -> port1; 12 + driver -> port2; 13 + driver -> port3:e; 14 + driver -> port4 [color=red]; 15 + 16 + payload1:s -> port1:e; 17 + payload2:s -> port3:e; 18 + edge [style=""]; 19 + 20 + subgraph cluster_topology { 21 + label="Topology Manager"; 22 + labelloc=bottom; 23 + 24 + /* Topology references */ 25 + mstb1 -> {port1, port2}; 26 + port1 -> mstb2; 27 + edge [color=red]; 28 + port2 -> mstb3 -> {port3, port4}; 29 + port3 -> mstb4; 30 + edge [color=""]; 31 + 32 + /* Malloc references */ 33 + edge [style=dashed;dir=back]; 34 + mstb1 -> {port1, port2}; 35 + port1 -> mstb2; 36 + port2 -> mstb3 -> port3; 37 + edge [color=red]; 38 + mstb3 -> port4; 39 + port3 -> mstb4; 40 + } 41 + 42 + mstb1 [label="MSTB #1";style=filled;fillcolor=palegreen]; 43 + mstb2 [label="MSTB #2";style=filled;fillcolor=palegreen]; 44 + mstb3 [label="MSTB #3";style=filled;fillcolor=palegreen]; 45 + mstb4 [label="MSTB #4";style=filled;fillcolor=grey]; 46 + 47 + port1 [label="Port #1"]; 48 + port2 [label="Port #2"]; 49 + port3 [label="Port #3"]; 50 + port4 [label="Port #4";style=filled;fillcolor=grey]; 51 + 52 + driver [label="DRM driver";style=filled;shape=box;fillcolor=lightblue]; 53 + 54 + payload1 [label="Payload #1";style=filled;shape=box;fillcolor=lightblue]; 55 + payload2 [label="Payload #2";style=filled;shape=box;fillcolor=lightblue]; 56 + }
+59
Documentation/gpu/dp-mst/topology-figure-3.dot
··· 1 + digraph T { 2 + /* Make sure our payloads are always drawn below the driver node */ 3 + subgraph cluster_driver { 4 + fillcolor = grey; 5 + style = filled; 6 + edge [dir=none]; 7 + driver -> payload1; 8 + driver -> payload2 [penwidth=3]; 9 + edge [dir=""]; 10 + } 11 + 12 + /* Driver malloc references */ 13 + edge [style=dashed]; 14 + driver -> port1; 15 + driver -> port2; 16 + driver -> port3:e; 17 + driver -> port4 [color=grey]; 18 + payload1:s -> port1:e; 19 + payload2:s -> port3:e [penwidth=3]; 20 + edge [style=""]; 21 + 22 + subgraph cluster_topology { 23 + label="Topology Manager"; 24 + labelloc=bottom; 25 + 26 + /* Topology references */ 27 + mstb1 -> {port1, port2}; 28 + port1 -> mstb2; 29 + edge [color=grey]; 30 + port2 -> mstb3 -> {port3, port4}; 31 + port3 -> mstb4; 32 + edge [color=""]; 33 + 34 + /* Malloc references */ 35 + edge [style=dashed;dir=back]; 36 + mstb1 -> {port1, port2}; 37 + port1 -> mstb2; 38 + port2 -> mstb3 [penwidth=3]; 39 + mstb3 -> port3 [penwidth=3]; 40 + edge [color=grey]; 41 + mstb3 -> port4; 42 + port3 -> mstb4; 43 + } 44 + 45 + mstb1 [label="MSTB #1";style=filled;fillcolor=palegreen]; 46 + mstb2 [label="MSTB #2";style=filled;fillcolor=palegreen]; 47 + mstb3 [label="MSTB #3";style=filled;fillcolor=palegreen;penwidth=3]; 48 + mstb4 [label="MSTB #4";style=filled;fillcolor=grey]; 49 + 50 + port1 [label="Port #1"]; 51 + port2 [label="Port #2";penwidth=5]; 52 + port3 [label="Port #3";penwidth=3]; 53 + port4 [label="Port #4";style=filled;fillcolor=grey]; 54 + 55 + driver [label="DRM driver";style=filled;shape=box;fillcolor=lightblue]; 56 + 57 + payload1 [label="Payload #1";style=filled;shape=box;fillcolor=lightblue]; 58 + payload2 [label="Payload #2";style=filled;shape=box;fillcolor=lightblue;penwidth=3]; 59 + }
+24 -2
Documentation/gpu/drm-kms-helpers.rst
··· 208 208 .. kernel-doc:: drivers/gpu/drm/drm_dp_dual_mode_helper.c 209 209 :export: 210 210 211 - Display Port MST Helper Functions Reference 212 - =========================================== 211 + Display Port MST Helpers 212 + ======================== 213 + 214 + Overview 215 + -------- 213 216 214 217 .. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c 215 218 :doc: dp mst helper 219 + 220 + .. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c 221 + :doc: Branch device and port refcounting 222 + 223 + Functions Reference 224 + ------------------- 216 225 217 226 .. kernel-doc:: include/drm/drm_dp_mst_helper.h 218 227 :internal: 219 228 220 229 .. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c 221 230 :export: 231 + 232 + Topology Lifetime Internals 233 + --------------------------- 234 + 235 + These functions aren't exported to drivers, but are documented here to help make 236 + the MST topology helpers easier to understand 237 + 238 + .. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c 239 + :functions: drm_dp_mst_topology_try_get_mstb drm_dp_mst_topology_get_mstb 240 + drm_dp_mst_topology_put_mstb 241 + drm_dp_mst_topology_try_get_port drm_dp_mst_topology_get_port 242 + drm_dp_mst_topology_put_port 243 + drm_dp_mst_get_mstb_malloc drm_dp_mst_put_mstb_malloc 222 244 223 245 MIPI DSI Helper Functions Reference 224 246 ===================================
+395 -63
drivers/gpu/drm/drm_dp_mst_topology.c
··· 850 850 if (lct > 1) 851 851 memcpy(mstb->rad, rad, lct / 2); 852 852 INIT_LIST_HEAD(&mstb->ports); 853 - kref_init(&mstb->kref); 853 + kref_init(&mstb->topology_kref); 854 + kref_init(&mstb->malloc_kref); 854 855 return mstb; 855 856 } 856 857 857 - static void drm_dp_free_mst_port(struct kref *kref); 858 - 859 858 static void drm_dp_free_mst_branch_device(struct kref *kref) 860 859 { 861 - struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref); 862 - if (mstb->port_parent) { 863 - if (list_empty(&mstb->port_parent->next)) 864 - kref_put(&mstb->port_parent->kref, drm_dp_free_mst_port); 865 - } 860 + struct drm_dp_mst_branch *mstb = 861 + container_of(kref, struct drm_dp_mst_branch, malloc_kref); 862 + 863 + if (mstb->port_parent) 864 + drm_dp_mst_put_port_malloc(mstb->port_parent); 865 + 866 866 kfree(mstb); 867 867 } 868 868 869 + /** 870 + * DOC: Branch device and port refcounting 871 + * 872 + * Topology refcount overview 873 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~ 874 + * 875 + * The refcounting schemes for &struct drm_dp_mst_branch and &struct 876 + * drm_dp_mst_port are somewhat unusual. Both ports and branch devices have 877 + * two different kinds of refcounts: topology refcounts, and malloc refcounts. 878 + * 879 + * Topology refcounts are not exposed to drivers, and are handled internally 880 + * by the DP MST helpers. The helpers use them in order to prevent the 881 + * in-memory topology state from being changed in the middle of critical 882 + * operations like changing the internal state of payload allocations. This 883 + * means each branch and port will be considered to be connected to the rest 884 + * of the topology until it's topology refcount reaches zero. Additionally, 885 + * for ports this means that their associated &struct drm_connector will stay 886 + * registered with userspace until the port's refcount reaches 0. 887 + * 888 + * Malloc refcount overview 889 + * ~~~~~~~~~~~~~~~~~~~~~~~~ 890 + * 891 + * Malloc references are used to keep a &struct drm_dp_mst_port or &struct 892 + * drm_dp_mst_branch allocated even after all of its topology references have 893 + * been dropped, so that the driver or MST helpers can safely access each 894 + * branch's last known state before it was disconnected from the topology. 895 + * When the malloc refcount of a port or branch reaches 0, the memory 896 + * allocation containing the &struct drm_dp_mst_branch or &struct 897 + * drm_dp_mst_port respectively will be freed. 898 + * 899 + * For &struct drm_dp_mst_branch, malloc refcounts are not currently exposed 900 + * to drivers. As of writing this documentation, there are no drivers that 901 + * have a usecase for accessing &struct drm_dp_mst_branch outside of the MST 902 + * helpers. Exposing this API to drivers in a race-free manner would take more 903 + * tweaking of the refcounting scheme, however patches are welcome provided 904 + * there is a legitimate driver usecase for this. 905 + * 906 + * Refcount relationships in a topology 907 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 908 + * 909 + * Let's take a look at why the relationship between topology and malloc 910 + * refcounts is designed the way it is. 911 + * 912 + * .. kernel-figure:: dp-mst/topology-figure-1.dot 913 + * 914 + * An example of topology and malloc refs in a DP MST topology with two 915 + * active payloads. Topology refcount increments are indicated by solid 916 + * lines, and malloc refcount increments are indicated by dashed lines. 917 + * Each starts from the branch which incremented the refcount, and ends at 918 + * the branch to which the refcount belongs to, i.e. the arrow points the 919 + * same way as the C pointers used to reference a structure. 920 + * 921 + * As you can see in the above figure, every branch increments the topology 922 + * refcount of it's children, and increments the malloc refcount of it's 923 + * parent. Additionally, every payload increments the malloc refcount of it's 924 + * assigned port by 1. 925 + * 926 + * So, what would happen if MSTB #3 from the above figure was unplugged from 927 + * the system, but the driver hadn't yet removed payload #2 from port #3? The 928 + * topology would start to look like the figure below. 929 + * 930 + * .. kernel-figure:: dp-mst/topology-figure-2.dot 931 + * 932 + * Ports and branch devices which have been released from memory are 933 + * colored grey, and references which have been removed are colored red. 934 + * 935 + * Whenever a port or branch device's topology refcount reaches zero, it will 936 + * decrement the topology refcounts of all its children, the malloc refcount 937 + * of its parent, and finally its own malloc refcount. For MSTB #4 and port 938 + * #4, this means they both have been disconnected from the topology and freed 939 + * from memory. But, because payload #2 is still holding a reference to port 940 + * #3, port #3 is removed from the topology but it's &struct drm_dp_mst_port 941 + * is still accessible from memory. This also means port #3 has not yet 942 + * decremented the malloc refcount of MSTB #3, so it's &struct 943 + * drm_dp_mst_branch will also stay allocated in memory until port #3's 944 + * malloc refcount reaches 0. 945 + * 946 + * This relationship is necessary because in order to release payload #2, we 947 + * need to be able to figure out the last relative of port #3 that's still 948 + * connected to the topology. In this case, we would travel up the topology as 949 + * shown below. 950 + * 951 + * .. kernel-figure:: dp-mst/topology-figure-3.dot 952 + * 953 + * And finally, remove payload #2 by communicating with port #2 through 954 + * sideband transactions. 955 + */ 956 + 957 + /** 958 + * drm_dp_mst_get_mstb_malloc() - Increment the malloc refcount of a branch 959 + * device 960 + * @mstb: The &struct drm_dp_mst_branch to increment the malloc refcount of 961 + * 962 + * Increments &drm_dp_mst_branch.malloc_kref. When 963 + * &drm_dp_mst_branch.malloc_kref reaches 0, the memory allocation for @mstb 964 + * will be released and @mstb may no longer be used. 965 + * 966 + * See also: drm_dp_mst_put_mstb_malloc() 967 + */ 968 + static void 969 + drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch *mstb) 970 + { 971 + kref_get(&mstb->malloc_kref); 972 + DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref)); 973 + } 974 + 975 + /** 976 + * drm_dp_mst_put_mstb_malloc() - Decrement the malloc refcount of a branch 977 + * device 978 + * @mstb: The &struct drm_dp_mst_branch to decrement the malloc refcount of 979 + * 980 + * Decrements &drm_dp_mst_branch.malloc_kref. When 981 + * &drm_dp_mst_branch.malloc_kref reaches 0, the memory allocation for @mstb 982 + * will be released and @mstb may no longer be used. 983 + * 984 + * See also: drm_dp_mst_get_mstb_malloc() 985 + */ 986 + static void 987 + drm_dp_mst_put_mstb_malloc(struct drm_dp_mst_branch *mstb) 988 + { 989 + DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref) - 1); 990 + kref_put(&mstb->malloc_kref, drm_dp_free_mst_branch_device); 991 + } 992 + 993 + static void drm_dp_free_mst_port(struct kref *kref) 994 + { 995 + struct drm_dp_mst_port *port = 996 + container_of(kref, struct drm_dp_mst_port, malloc_kref); 997 + 998 + drm_dp_mst_put_mstb_malloc(port->parent); 999 + kfree(port); 1000 + } 1001 + 1002 + /** 1003 + * drm_dp_mst_get_port_malloc() - Increment the malloc refcount of an MST port 1004 + * @port: The &struct drm_dp_mst_port to increment the malloc refcount of 1005 + * 1006 + * Increments &drm_dp_mst_port.malloc_kref. When &drm_dp_mst_port.malloc_kref 1007 + * reaches 0, the memory allocation for @port will be released and @port may 1008 + * no longer be used. 1009 + * 1010 + * Because @port could potentially be freed at any time by the DP MST helpers 1011 + * if &drm_dp_mst_port.malloc_kref reaches 0, including during a call to this 1012 + * function, drivers that which to make use of &struct drm_dp_mst_port should 1013 + * ensure that they grab at least one main malloc reference to their MST ports 1014 + * in &drm_dp_mst_topology_cbs.add_connector. This callback is called before 1015 + * there is any chance for &drm_dp_mst_port.malloc_kref to reach 0. 1016 + * 1017 + * See also: drm_dp_mst_put_port_malloc() 1018 + */ 1019 + void 1020 + drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port) 1021 + { 1022 + kref_get(&port->malloc_kref); 1023 + DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->malloc_kref)); 1024 + } 1025 + EXPORT_SYMBOL(drm_dp_mst_get_port_malloc); 1026 + 1027 + /** 1028 + * drm_dp_mst_put_port_malloc() - Decrement the malloc refcount of an MST port 1029 + * @port: The &struct drm_dp_mst_port to decrement the malloc refcount of 1030 + * 1031 + * Decrements &drm_dp_mst_port.malloc_kref. When &drm_dp_mst_port.malloc_kref 1032 + * reaches 0, the memory allocation for @port will be released and @port may 1033 + * no longer be used. 1034 + * 1035 + * See also: drm_dp_mst_get_port_malloc() 1036 + */ 1037 + void 1038 + drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port) 1039 + { 1040 + DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->malloc_kref) - 1); 1041 + kref_put(&port->malloc_kref, drm_dp_free_mst_port); 1042 + } 1043 + EXPORT_SYMBOL(drm_dp_mst_put_port_malloc); 1044 + 869 1045 static void drm_dp_destroy_mst_branch_device(struct kref *kref) 870 1046 { 871 - struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref); 1047 + struct drm_dp_mst_branch *mstb = 1048 + container_of(kref, struct drm_dp_mst_branch, topology_kref); 1049 + struct drm_dp_mst_topology_mgr *mgr = mstb->mgr; 872 1050 struct drm_dp_mst_port *port, *tmp; 873 1051 bool wake_tx = false; 874 1052 875 - /* 876 - * init kref again to be used by ports to remove mst branch when it is 877 - * not needed anymore 878 - */ 879 - kref_init(kref); 880 - 881 - if (mstb->port_parent && list_empty(&mstb->port_parent->next)) 882 - kref_get(&mstb->port_parent->kref); 883 - 884 - /* 885 - * destroy all ports - don't need lock 886 - * as there are no more references to the mst branch 887 - * device at this point. 888 - */ 1053 + mutex_lock(&mgr->lock); 889 1054 list_for_each_entry_safe(port, tmp, &mstb->ports, next) { 890 1055 list_del(&port->next); 891 1056 drm_dp_mst_topology_put_port(port); 892 1057 } 1058 + mutex_unlock(&mgr->lock); 893 1059 894 1060 /* drop any tx slots msg */ 895 1061 mutex_lock(&mstb->mgr->qlock); ··· 1074 908 if (wake_tx) 1075 909 wake_up_all(&mstb->mgr->tx_waitq); 1076 910 1077 - kref_put(kref, drm_dp_free_mst_branch_device); 911 + drm_dp_mst_put_mstb_malloc(mstb); 1078 912 } 1079 913 1080 - static void drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb) 914 + /** 915 + * drm_dp_mst_topology_try_get_mstb() - Increment the topology refcount of a 916 + * branch device unless its zero 917 + * @mstb: &struct drm_dp_mst_branch to increment the topology refcount of 918 + * 919 + * Attempts to grab a topology reference to @mstb, if it hasn't yet been 920 + * removed from the topology (e.g. &drm_dp_mst_branch.topology_kref has 921 + * reached 0). Holding a topology reference implies that a malloc reference 922 + * will be held to @mstb as long as the user holds the topology reference. 923 + * 924 + * Care should be taken to ensure that the user has at least one malloc 925 + * reference to @mstb. If you already have a topology reference to @mstb, you 926 + * should use drm_dp_mst_topology_get_mstb() instead. 927 + * 928 + * See also: 929 + * drm_dp_mst_topology_get_mstb() 930 + * drm_dp_mst_topology_put_mstb() 931 + * 932 + * Returns: 933 + * * 1: A topology reference was grabbed successfully 934 + * * 0: @port is no longer in the topology, no reference was grabbed 935 + */ 936 + static int __must_check 937 + drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb) 1081 938 { 1082 - kref_put(&mstb->kref, drm_dp_destroy_mst_branch_device); 939 + int ret = kref_get_unless_zero(&mstb->topology_kref); 940 + 941 + if (ret) 942 + DRM_DEBUG("mstb %p (%d)\n", mstb, 943 + kref_read(&mstb->topology_kref)); 944 + 945 + return ret; 1083 946 } 1084 947 948 + /** 949 + * drm_dp_mst_topology_get_mstb() - Increment the topology refcount of a 950 + * branch device 951 + * @mstb: The &struct drm_dp_mst_branch to increment the topology refcount of 952 + * 953 + * Increments &drm_dp_mst_branch.topology_refcount without checking whether or 954 + * not it's already reached 0. This is only valid to use in scenarios where 955 + * you are already guaranteed to have at least one active topology reference 956 + * to @mstb. Otherwise, drm_dp_mst_topology_try_get_mstb() must be used. 957 + * 958 + * See also: 959 + * drm_dp_mst_topology_try_get_mstb() 960 + * drm_dp_mst_topology_put_mstb() 961 + */ 962 + static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb) 963 + { 964 + WARN_ON(kref_read(&mstb->topology_kref) == 0); 965 + kref_get(&mstb->topology_kref); 966 + DRM_DEBUG("mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref)); 967 + } 968 + 969 + /** 970 + * drm_dp_mst_topology_put_mstb() - release a topology reference to a branch 971 + * device 972 + * @mstb: The &struct drm_dp_mst_branch to release the topology reference from 973 + * 974 + * Releases a topology reference from @mstb by decrementing 975 + * &drm_dp_mst_branch.topology_kref. 976 + * 977 + * See also: 978 + * drm_dp_mst_topology_try_get_mstb() 979 + * drm_dp_mst_topology_get_mstb() 980 + */ 981 + static void 982 + drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb) 983 + { 984 + DRM_DEBUG("mstb %p (%d)\n", 985 + mstb, kref_read(&mstb->topology_kref) - 1); 986 + kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device); 987 + } 1085 988 1086 989 static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt) 1087 990 { ··· 1172 937 1173 938 static void drm_dp_destroy_port(struct kref *kref) 1174 939 { 1175 - struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref); 940 + struct drm_dp_mst_port *port = 941 + container_of(kref, struct drm_dp_mst_port, topology_kref); 1176 942 struct drm_dp_mst_topology_mgr *mgr = port->mgr; 1177 943 1178 944 if (!port->input) { ··· 1192 956 * from an EDID retrieval */ 1193 957 1194 958 mutex_lock(&mgr->destroy_connector_lock); 1195 - kref_get(&port->parent->kref); 1196 959 list_add(&port->next, &mgr->destroy_connector_list); 1197 960 mutex_unlock(&mgr->destroy_connector_lock); 1198 961 schedule_work(&mgr->destroy_connector_work); ··· 1202 967 drm_dp_port_teardown_pdt(port, port->pdt); 1203 968 port->pdt = DP_PEER_DEVICE_NONE; 1204 969 } 1205 - kfree(port); 970 + drm_dp_mst_put_port_malloc(port); 1206 971 } 1207 972 973 + /** 974 + * drm_dp_mst_topology_try_get_port() - Increment the topology refcount of a 975 + * port unless its zero 976 + * @port: &struct drm_dp_mst_port to increment the topology refcount of 977 + * 978 + * Attempts to grab a topology reference to @port, if it hasn't yet been 979 + * removed from the topology (e.g. &drm_dp_mst_port.topology_kref has reached 980 + * 0). Holding a topology reference implies that a malloc reference will be 981 + * held to @port as long as the user holds the topology reference. 982 + * 983 + * Care should be taken to ensure that the user has at least one malloc 984 + * reference to @port. If you already have a topology reference to @port, you 985 + * should use drm_dp_mst_topology_get_port() instead. 986 + * 987 + * See also: 988 + * drm_dp_mst_topology_get_port() 989 + * drm_dp_mst_topology_put_port() 990 + * 991 + * Returns: 992 + * * 1: A topology reference was grabbed successfully 993 + * * 0: @port is no longer in the topology, no reference was grabbed 994 + */ 995 + static int __must_check 996 + drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port) 997 + { 998 + int ret = kref_get_unless_zero(&port->topology_kref); 999 + 1000 + if (ret) 1001 + DRM_DEBUG("port %p (%d)\n", port, 1002 + kref_read(&port->topology_kref)); 1003 + 1004 + return ret; 1005 + } 1006 + 1007 + /** 1008 + * drm_dp_mst_topology_get_port() - Increment the topology refcount of a port 1009 + * @port: The &struct drm_dp_mst_port to increment the topology refcount of 1010 + * 1011 + * Increments &drm_dp_mst_port.topology_refcount without checking whether or 1012 + * not it's already reached 0. This is only valid to use in scenarios where 1013 + * you are already guaranteed to have at least one active topology reference 1014 + * to @port. Otherwise, drm_dp_mst_topology_try_get_port() must be used. 1015 + * 1016 + * See also: 1017 + * drm_dp_mst_topology_try_get_port() 1018 + * drm_dp_mst_topology_put_port() 1019 + */ 1020 + static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port) 1021 + { 1022 + WARN_ON(kref_read(&port->topology_kref) == 0); 1023 + kref_get(&port->topology_kref); 1024 + DRM_DEBUG("port %p (%d)\n", port, kref_read(&port->topology_kref)); 1025 + } 1026 + 1027 + /** 1028 + * drm_dp_mst_topology_put_port() - release a topology reference to a port 1029 + * @port: The &struct drm_dp_mst_port to release the topology reference from 1030 + * 1031 + * Releases a topology reference from @port by decrementing 1032 + * &drm_dp_mst_port.topology_kref. 1033 + * 1034 + * See also: 1035 + * drm_dp_mst_topology_try_get_port() 1036 + * drm_dp_mst_topology_get_port() 1037 + */ 1208 1038 static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port) 1209 1039 { 1210 - kref_put(&port->kref, drm_dp_destroy_port); 1040 + DRM_DEBUG("port %p (%d)\n", 1041 + port, kref_read(&port->topology_kref) - 1); 1042 + kref_put(&port->topology_kref, drm_dp_destroy_port); 1211 1043 } 1212 1044 1213 1045 static struct drm_dp_mst_branch * ··· 1283 981 { 1284 982 struct drm_dp_mst_port *port; 1285 983 struct drm_dp_mst_branch *rmstb; 1286 - if (to_find == mstb) { 1287 - kref_get(&mstb->kref); 984 + 985 + if (to_find == mstb) 1288 986 return mstb; 1289 - } 987 + 1290 988 list_for_each_entry(port, &mstb->ports, next) { 1291 989 if (port->mstb) { 1292 990 rmstb = drm_dp_mst_topology_get_mstb_validated_locked( ··· 1303 1001 struct drm_dp_mst_branch *mstb) 1304 1002 { 1305 1003 struct drm_dp_mst_branch *rmstb = NULL; 1004 + 1306 1005 mutex_lock(&mgr->lock); 1307 - if (mgr->mst_primary) 1006 + if (mgr->mst_primary) { 1308 1007 rmstb = drm_dp_mst_topology_get_mstb_validated_locked( 1309 1008 mgr->mst_primary, mstb); 1009 + 1010 + if (rmstb && !drm_dp_mst_topology_try_get_mstb(rmstb)) 1011 + rmstb = NULL; 1012 + } 1310 1013 mutex_unlock(&mgr->lock); 1311 1014 return rmstb; 1312 1015 } 1313 1016 1314 - static struct drm_dp_mst_port *drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *to_find) 1017 + static struct drm_dp_mst_port * 1018 + drm_dp_mst_topology_get_port_validated_locked(struct drm_dp_mst_branch *mstb, 1019 + struct drm_dp_mst_port *to_find) 1315 1020 { 1316 1021 struct drm_dp_mst_port *port, *mport; 1317 1022 1318 1023 list_for_each_entry(port, &mstb->ports, next) { 1319 - if (port == to_find) { 1320 - kref_get(&port->kref); 1024 + if (port == to_find) 1321 1025 return port; 1322 - } 1026 + 1323 1027 if (port->mstb) { 1324 - mport = drm_dp_mst_get_port_ref_locked(port->mstb, to_find); 1028 + mport = drm_dp_mst_topology_get_port_validated_locked( 1029 + port->mstb, to_find); 1325 1030 if (mport) 1326 1031 return mport; 1327 1032 } ··· 1341 1032 struct drm_dp_mst_port *port) 1342 1033 { 1343 1034 struct drm_dp_mst_port *rport = NULL; 1035 + 1344 1036 mutex_lock(&mgr->lock); 1345 - if (mgr->mst_primary) 1346 - rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, port); 1037 + if (mgr->mst_primary) { 1038 + rport = drm_dp_mst_topology_get_port_validated_locked( 1039 + mgr->mst_primary, port); 1040 + 1041 + if (rport && !drm_dp_mst_topology_try_get_port(rport)) 1042 + rport = NULL; 1043 + } 1347 1044 mutex_unlock(&mgr->lock); 1348 1045 return rport; 1349 1046 } ··· 1357 1042 static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num) 1358 1043 { 1359 1044 struct drm_dp_mst_port *port; 1045 + int ret; 1360 1046 1361 1047 list_for_each_entry(port, &mstb->ports, next) { 1362 1048 if (port->port_num == port_num) { 1363 - kref_get(&port->kref); 1364 - return port; 1049 + ret = drm_dp_mst_topology_try_get_port(port); 1050 + return ret ? port : NULL; 1365 1051 } 1366 1052 } 1367 1053 ··· 1411 1095 if (port->mstb) { 1412 1096 port->mstb->mgr = port->mgr; 1413 1097 port->mstb->port_parent = port; 1098 + /* 1099 + * Make sure this port's memory allocation stays 1100 + * around until it's child MSTB releases it 1101 + */ 1102 + drm_dp_mst_get_port_malloc(port); 1414 1103 1415 1104 send_link = true; 1416 1105 } ··· 1476 1155 bool created = false; 1477 1156 int old_pdt = 0; 1478 1157 int old_ddps = 0; 1158 + 1479 1159 port = drm_dp_get_port(mstb, port_msg->port_number); 1480 1160 if (!port) { 1481 1161 port = kzalloc(sizeof(*port), GFP_KERNEL); 1482 1162 if (!port) 1483 1163 return; 1484 - kref_init(&port->kref); 1164 + kref_init(&port->topology_kref); 1165 + kref_init(&port->malloc_kref); 1485 1166 port->parent = mstb; 1486 1167 port->port_num = port_msg->port_number; 1487 1168 port->mgr = mstb->mgr; 1488 1169 port->aux.name = "DPMST"; 1489 1170 port->aux.dev = dev->dev; 1171 + 1172 + /* 1173 + * Make sure the memory allocation for our parent branch stays 1174 + * around until our own memory allocation is released 1175 + */ 1176 + drm_dp_mst_get_mstb_malloc(mstb); 1177 + 1490 1178 created = true; 1491 1179 } else { 1492 1180 old_pdt = port->pdt; ··· 1515 1185 for this list */ 1516 1186 if (created) { 1517 1187 mutex_lock(&mstb->mgr->lock); 1518 - kref_get(&port->kref); 1188 + drm_dp_mst_topology_get_port(port); 1519 1189 list_add(&port->next, &mstb->ports); 1520 1190 mutex_unlock(&mstb->mgr->lock); 1521 1191 } ··· 1614 1284 { 1615 1285 struct drm_dp_mst_branch *mstb; 1616 1286 struct drm_dp_mst_port *port; 1617 - int i; 1287 + int i, ret; 1618 1288 /* find the port by iterating down */ 1619 1289 1620 1290 mutex_lock(&mgr->lock); ··· 1639 1309 } 1640 1310 } 1641 1311 } 1642 - kref_get(&mstb->kref); 1312 + ret = drm_dp_mst_topology_try_get_mstb(mstb); 1313 + if (!ret) 1314 + mstb = NULL; 1643 1315 out: 1644 1316 mutex_unlock(&mgr->lock); 1645 1317 return mstb; ··· 1676 1344 uint8_t *guid) 1677 1345 { 1678 1346 struct drm_dp_mst_branch *mstb; 1347 + int ret; 1679 1348 1680 1349 /* find the port by iterating down */ 1681 1350 mutex_lock(&mgr->lock); 1682 1351 1683 1352 mstb = get_mst_branch_device_by_guid_helper(mgr->mst_primary, guid); 1684 - 1685 - if (mstb) 1686 - kref_get(&mstb->kref); 1353 + if (mstb) { 1354 + ret = drm_dp_mst_topology_try_get_mstb(mstb); 1355 + if (!ret) 1356 + mstb = NULL; 1357 + } 1687 1358 1688 1359 mutex_unlock(&mgr->lock); 1689 1360 return mstb; ··· 1725 1390 { 1726 1391 struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work); 1727 1392 struct drm_dp_mst_branch *mstb; 1393 + int ret; 1728 1394 1729 1395 mutex_lock(&mgr->lock); 1730 1396 mstb = mgr->mst_primary; 1731 1397 if (mstb) { 1732 - kref_get(&mstb->kref); 1398 + ret = drm_dp_mst_topology_try_get_mstb(mstb); 1399 + if (!ret) 1400 + mstb = NULL; 1733 1401 } 1734 1402 mutex_unlock(&mgr->lock); 1735 1403 if (mstb) { ··· 2060 1722 2061 1723 if (found_port) { 2062 1724 rmstb = found_port->parent; 2063 - kref_get(&rmstb->kref); 2064 - *port_num = found_port->port_num; 1725 + if (drm_dp_mst_topology_try_get_mstb(rmstb)) 1726 + *port_num = found_port->port_num; 1727 + else 1728 + rmstb = NULL; 2065 1729 } 2066 1730 } 2067 1731 mutex_unlock(&mgr->lock); ··· 2516 2176 2517 2177 /* give this the main reference */ 2518 2178 mgr->mst_primary = mstb; 2519 - kref_get(&mgr->mst_primary->kref); 2179 + drm_dp_mst_topology_get_mstb(mgr->mst_primary); 2520 2180 2521 2181 ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 2522 2182 DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC); ··· 3436 3096 mutex_unlock(&mgr->qlock); 3437 3097 } 3438 3098 3439 - static void drm_dp_free_mst_port(struct kref *kref) 3440 - { 3441 - struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref); 3442 - kref_put(&port->parent->kref, drm_dp_free_mst_branch_device); 3443 - kfree(port); 3444 - } 3445 - 3446 3099 static void drm_dp_destroy_connector_work(struct work_struct *work) 3447 3100 { 3448 3101 struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work); ··· 3456 3123 list_del(&port->next); 3457 3124 mutex_unlock(&mgr->destroy_connector_lock); 3458 3125 3459 - kref_init(&port->kref); 3460 3126 INIT_LIST_HEAD(&port->next); 3461 3127 3462 3128 mgr->cbs->destroy_connector(mgr, port->connector); ··· 3469 3137 drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); 3470 3138 } 3471 3139 3472 - kref_put(&port->kref, drm_dp_free_mst_port); 3140 + drm_dp_mst_put_port_malloc(port); 3473 3141 send_hotplug = true; 3474 3142 } 3475 3143 if (send_hotplug)
+28 -4
include/drm/drm_dp_mst_helper.h
··· 44 44 45 45 /** 46 46 * struct drm_dp_mst_port - MST port 47 - * @kref: reference count for this port. 48 47 * @port_num: port number 49 48 * @input: if this port is an input port. 50 49 * @mcs: message capability status - DP 1.2 spec. ··· 66 67 * in the MST topology. 67 68 */ 68 69 struct drm_dp_mst_port { 69 - struct kref kref; 70 + /** 71 + * @topology_kref: refcount for this port's lifetime in the topology, 72 + * only the DP MST helpers should need to touch this 73 + */ 74 + struct kref topology_kref; 75 + 76 + /** 77 + * @malloc_kref: refcount for the memory allocation containing this 78 + * structure. See drm_dp_mst_get_port_malloc() and 79 + * drm_dp_mst_put_port_malloc(). 80 + */ 81 + struct kref malloc_kref; 70 82 71 83 u8 port_num; 72 84 bool input; ··· 112 102 113 103 /** 114 104 * struct drm_dp_mst_branch - MST branch device. 115 - * @kref: reference count for this port. 116 105 * @rad: Relative Address to talk to this branch device. 117 106 * @lct: Link count total to talk to this branch device. 118 107 * @num_ports: number of ports on the branch. ··· 130 121 * to downstream port of parent branches. 131 122 */ 132 123 struct drm_dp_mst_branch { 133 - struct kref kref; 124 + /** 125 + * @topology_kref: refcount for this branch device's lifetime in the 126 + * topology, only the DP MST helpers should need to touch this 127 + */ 128 + struct kref topology_kref; 129 + 130 + /** 131 + * @malloc_kref: refcount for the memory allocation containing this 132 + * structure. See drm_dp_mst_get_mstb_malloc() and 133 + * drm_dp_mst_put_mstb_malloc(). 134 + */ 135 + struct kref malloc_kref; 136 + 134 137 u8 rad[8]; 135 138 u8 lct; 136 139 int num_ports; ··· 646 625 int slots); 647 626 int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr, 648 627 struct drm_dp_mst_port *port, bool power_up); 628 + 629 + void drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port); 630 + void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port); 649 631 650 632 #endif