From 973a5909e99d0301a8832ca0cf07f9be01c4e97a Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 23 Apr 2020 12:42:24 -0400 Subject: Revert "drm/dp_mst: Remove single tx msg restriction." This reverts commit 6bb0942e8f46863a745489cce27efe5be2a3885e. Unfortunately it would appear that the rumors we've heard of sideband message interleaving not being very well supported are true. On the Lenovo ThinkPad Thunderbolt 3 dock that I have, interleaved messages appear to just get dropped: [drm:drm_dp_mst_wait_tx_reply [drm_kms_helper]] timedout msg send 00000000571ddfd0 2 1 [dp_mst] txmsg cur_offset=2 cur_len=2 seqno=1 state=SENT path_msg=1 dst=00 [dp_mst] type=ENUM_PATH_RESOURCES contents: [dp_mst] port=2 DP descriptor for this hub: OUI 90-cc-24 dev-ID SYNA3 HW-rev 1.0 SW-rev 3.12 quirks 0x0008 It would seem like as well that this is a somewhat well known issue in the field. From section 5.4.2 of the DisplayPort 2.0 specification: There are MST Sink/Branch devices in the field that do not handle interleaved message transactions. To facilitate message transaction handling by downstream devices, an MST Source device shall generate message transactions in an atomic manner (i.e., the MST Source device shall not concurrently interleave multiple message transactions). Therefore, an MST Source device shall clear the Message_Sequence_No value in the Sideband_MSG_Header to 0. MST Source devices that support field policy updates by way of software should update the policy to forego the generation of interleaved message transactions. This is a bit disappointing, as features like HDCP require that we send a sideband request every ~2 seconds for each active stream. However, there isn't really anything in the specification that allows us to accurately probe for interleaved messages. If it ends up being that we -really- need this in the future, we might be able to whitelist hubs where interleaving is known to work-or maybe try some sort of heuristics. But for now, let's just play it safe and not use it. Signed-off-by: Lyude Paul Fixes: 6bb0942e8f46 ("drm/dp_mst: Remove single tx msg restriction.") Cc: Wayne Lin Cc: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200423164225.680178-1-lyude@redhat.com Reviewed-by: Sean Paul --- include/drm/drm_dp_mst_helper.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 2d7c26592c05..96bcf33c03d3 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -592,6 +592,11 @@ struct drm_dp_mst_topology_mgr { */ bool payload_id_table_cleared : 1; + /** + * @is_waiting_for_dwn_reply: whether we're waiting for a down reply. + */ + bool is_waiting_for_dwn_reply : 1; + /** * @mst_primary: Pointer to the primary/first branch device. */ -- cgit v1.2.3 From d308a881a5917bdb46472c861a1dabe54b46c423 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 24 Apr 2020 14:13:08 -0400 Subject: drm/dp_mst: Kill the second sideband tx slot, save the world While we support using both tx slots for sideband transmissions, it appears that DisplayPort devices in the field didn't end up doing a very good job of supporting it. From section 5.2.1 of the DP 2.0 specification: There are MST Sink/Branch devices in the field that do not handle interleaved message transactions. To facilitate message transaction handling by downstream devices, an MST Source device shall generate message transactions in an atomic manner (i.e., the MST Source device shall not concurrently interleave multiple message transactions). Therefore, an MST Source device shall clear the Message_Sequence_No value in the Sideband_MSG_Header to 0. This might come as a bit of a surprise since the vast majority of hubs will support using both tx slots even if they don't support interleaved message transactions, and we've also been using both tx slots since MST was introduced into the kernel. However, there is one device we've had trouble getting working consistently with MST for so long that we actually assumed it was just broken: the infamous Dell P2415Qb. Previously this monitor would appear to work sometimes, but in most situations would end up timing out LINK_ADDRESS messages almost at random until you power cycled the whole display. After reading section 5.2.1 in the DP 2.0 spec, some closer investigation into this infamous display revealed it was only ever timing out on sideband messages in the second TX slot. Sure enough, avoiding the second TX slot has suddenly made this monitor function perfectly for the first time in five years. And since they explicitly mention this in the specification, I doubt this is the only monitor out there with this issue. This might even explain explain the seemingly harmless garbage sideband responses we would occasionally see with MST hubs! So - rewrite our sideband TX handlers to only support one TX slot. In order to simplify our sideband handling now that we don't support transmitting to multiple MSTBs at once, we also move all state tracking for down replies from mstbs to the topology manager. Signed-off-by: Lyude Paul Fixes: ad7f8a1f9ced ("drm/helper: add Displayport multi-stream helper (v0.6)") Cc: Sean Paul Cc: "Lin, Wayne" Cc: # v3.17+ Reviewed-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200424181308.770749-1-lyude@redhat.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 149 ++++++++++------------------------ include/drm/drm_dp_mst_helper.h | 29 ++----- 2 files changed, 50 insertions(+), 128 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index d2c19791b2b6..b90cca361afe 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1197,16 +1197,8 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, /* remove from q */ if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED || - txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) { + txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) list_del(&txmsg->next); - } - - if (txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND || - txmsg->state == DRM_DP_SIDEBAND_TX_SENT) { - mstb->tx_slots[txmsg->seqno] = NULL; - } - mgr->is_waiting_for_dwn_reply = false; - } out: if (unlikely(ret == -EIO) && drm_debug_enabled(DRM_UT_DP)) { @@ -2685,22 +2677,6 @@ static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr, struct drm_dp_mst_branch *mstb = txmsg->dst; u8 req_type; - /* both msg slots are full */ - if (txmsg->seqno == -1) { - if (mstb->tx_slots[0] && mstb->tx_slots[1]) { - DRM_DEBUG_KMS("%s: failed to find slot\n", __func__); - return -EAGAIN; - } - if (mstb->tx_slots[0] == NULL && mstb->tx_slots[1] == NULL) { - txmsg->seqno = mstb->last_seqno; - mstb->last_seqno ^= 1; - } else if (mstb->tx_slots[0] == NULL) - txmsg->seqno = 0; - else - txmsg->seqno = 1; - mstb->tx_slots[txmsg->seqno] = txmsg; - } - req_type = txmsg->msg[0] & 0x7f; if (req_type == DP_CONNECTION_STATUS_NOTIFY || req_type == DP_RESOURCE_STATUS_NOTIFY) @@ -2712,7 +2688,7 @@ static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr, hdr->lcr = mstb->lct - 1; if (mstb->lct > 1) memcpy(hdr->rad, mstb->rad, mstb->lct / 2); - hdr->seqno = txmsg->seqno; + return 0; } /* @@ -2727,15 +2703,15 @@ static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr, int len, space, idx, tosend; int ret; + if (txmsg->state == DRM_DP_SIDEBAND_TX_SENT) + return 0; + memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr)); - if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) { - txmsg->seqno = -1; + if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND; - } - /* make hdr from dst mst - for replies use seqno - otherwise assign one */ + /* make hdr from dst mst */ ret = set_hdr_from_dst_qlock(&hdr, txmsg); if (ret < 0) return ret; @@ -2788,42 +2764,17 @@ static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr) if (list_empty(&mgr->tx_msg_downq)) return; - txmsg = list_first_entry(&mgr->tx_msg_downq, struct drm_dp_sideband_msg_tx, next); + txmsg = list_first_entry(&mgr->tx_msg_downq, + struct drm_dp_sideband_msg_tx, next); ret = process_single_tx_qlock(mgr, txmsg, false); - if (ret == 1) { - /* txmsg is sent it should be in the slots now */ - mgr->is_waiting_for_dwn_reply = true; - list_del(&txmsg->next); - } else if (ret) { + if (ret < 0) { DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); - mgr->is_waiting_for_dwn_reply = false; list_del(&txmsg->next); - if (txmsg->seqno != -1) - txmsg->dst->tx_slots[txmsg->seqno] = NULL; txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT; wake_up_all(&mgr->tx_waitq); } } -/* called holding qlock */ -static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_sideband_msg_tx *txmsg) -{ - int ret; - - /* construct a chunk from the first msg in the tx_msg queue */ - ret = process_single_tx_qlock(mgr, txmsg, true); - - if (ret != 1) - DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); - - if (txmsg->seqno != -1) { - WARN_ON((unsigned int)txmsg->seqno > - ARRAY_SIZE(txmsg->dst->tx_slots)); - txmsg->dst->tx_slots[txmsg->seqno] = NULL; - } -} - static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_sideband_msg_tx *txmsg) { @@ -2836,8 +2787,7 @@ static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr, drm_dp_mst_dump_sideband_msg_tx(&p, txmsg); } - if (list_is_singular(&mgr->tx_msg_downq) && - !mgr->is_waiting_for_dwn_reply) + if (list_is_singular(&mgr->tx_msg_downq)) process_single_down_tx_qlock(mgr); mutex_unlock(&mgr->qlock); } @@ -3457,7 +3407,7 @@ static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb, - int req_type, int seqno, bool broadcast) + int req_type, bool broadcast) { struct drm_dp_sideband_msg_tx *txmsg; @@ -3466,13 +3416,11 @@ static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM; txmsg->dst = mstb; - txmsg->seqno = seqno; drm_dp_encode_up_ack_reply(txmsg, req_type); mutex_lock(&mgr->qlock); - - process_single_up_tx_qlock(mgr, txmsg); - + /* construct a chunk from the first msg in the tx_msg queue */ + process_single_tx_qlock(mgr, txmsg, true); mutex_unlock(&mgr->qlock); kfree(txmsg); @@ -3697,8 +3645,9 @@ out_fail: } EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume); -static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up, - struct drm_dp_mst_branch **mstb, int *seqno) +static bool +drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up, + struct drm_dp_mst_branch **mstb) { int len; u8 replyblock[32]; @@ -3706,13 +3655,13 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up, int ret; u8 hdrlen; struct drm_dp_sideband_msg_hdr hdr; - struct drm_dp_sideband_msg_rx *msg; + struct drm_dp_sideband_msg_rx *msg = + up ? &mgr->up_req_recv : &mgr->down_rep_recv; int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE; if (!up) *mstb = NULL; - *seqno = -1; len = min(mgr->max_dpcd_transaction_bytes, 16); ret = drm_dp_dpcd_read(mgr->aux, basereg, replyblock, len); @@ -3729,11 +3678,7 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up, return false; } - *seqno = hdr.seqno; - - if (up) { - msg = &mgr->up_req_recv; - } else { + if (!up) { /* Caller is responsible for giving back this reference */ *mstb = drm_dp_get_mst_branch_device(mgr, hdr.lct, hdr.rad); if (!*mstb) { @@ -3741,7 +3686,6 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up, hdr.lct); return false; } - msg = &(*mstb)->down_rep_recv[hdr.seqno]; } if (!drm_dp_sideband_msg_set_header(msg, &hdr, hdrlen)) { @@ -3785,13 +3729,10 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) { struct drm_dp_sideband_msg_tx *txmsg; struct drm_dp_mst_branch *mstb = NULL; - struct drm_dp_sideband_msg_rx *msg = NULL; - int seqno = -1; - - if (!drm_dp_get_one_sb_msg(mgr, false, &mstb, &seqno)) - goto out_clear_reply; + struct drm_dp_sideband_msg_rx *msg = &mgr->down_rep_recv; - msg = &mstb->down_rep_recv[seqno]; + if (!drm_dp_get_one_sb_msg(mgr, false, &mstb)) + goto out; /* Multi-packet message transmission, don't clear the reply */ if (!msg->have_eomt) @@ -3799,11 +3740,12 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) /* find the message */ mutex_lock(&mgr->qlock); - txmsg = mstb->tx_slots[seqno]; - /* remove from slots */ + txmsg = list_first_entry_or_null(&mgr->tx_msg_downq, + struct drm_dp_sideband_msg_tx, next); mutex_unlock(&mgr->qlock); - if (!txmsg) { + /* Were we actually expecting a response, and from this mstb? */ + if (!txmsg || txmsg->dst != mstb) { struct drm_dp_sideband_msg_hdr *hdr; hdr = &msg->initial_hdr; DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n", @@ -3828,8 +3770,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) mutex_lock(&mgr->qlock); txmsg->state = DRM_DP_SIDEBAND_TX_RX; - mstb->tx_slots[seqno] = NULL; - mgr->is_waiting_for_dwn_reply = false; + list_del(&txmsg->next); mutex_unlock(&mgr->qlock); wake_up_all(&mgr->tx_waitq); @@ -3837,11 +3778,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) return 0; out_clear_reply: - mutex_lock(&mgr->qlock); - mgr->is_waiting_for_dwn_reply = false; - mutex_unlock(&mgr->qlock); - if (msg) - memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx)); + memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx)); out: if (mstb) drm_dp_mst_topology_put_mstb(mstb); @@ -3921,9 +3858,8 @@ static void drm_dp_mst_up_req_work(struct work_struct *work) static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) { struct drm_dp_pending_up_req *up_req; - int seqno; - if (!drm_dp_get_one_sb_msg(mgr, true, NULL, &seqno)) + if (!drm_dp_get_one_sb_msg(mgr, true, NULL)) goto out; if (!mgr->up_req_recv.have_eomt) @@ -3947,7 +3883,7 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) } drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, up_req->msg.req_type, - seqno, false); + false); if (up_req->msg.req_type == DP_CONNECTION_STATUS_NOTIFY) { const struct drm_dp_connection_status_notify *conn_stat = @@ -4692,7 +4628,7 @@ static void drm_dp_tx_work(struct work_struct *work) struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work); mutex_lock(&mgr->qlock); - if (!list_empty(&mgr->tx_msg_downq) && !mgr->is_waiting_for_dwn_reply) + if (!list_empty(&mgr->tx_msg_downq)) process_single_down_tx_qlock(mgr); mutex_unlock(&mgr->qlock); } @@ -4713,26 +4649,25 @@ static inline void drm_dp_delayed_destroy_mstb(struct drm_dp_mst_branch *mstb) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr; - struct drm_dp_mst_port *port, *tmp; + struct drm_dp_mst_port *port, *port_tmp; + struct drm_dp_sideband_msg_tx *txmsg, *txmsg_tmp; bool wake_tx = false; mutex_lock(&mgr->lock); - list_for_each_entry_safe(port, tmp, &mstb->ports, next) { + list_for_each_entry_safe(port, port_tmp, &mstb->ports, next) { list_del(&port->next); drm_dp_mst_topology_put_port(port); } mutex_unlock(&mgr->lock); - /* drop any tx slots msg */ + /* drop any tx slot msg */ mutex_lock(&mstb->mgr->qlock); - if (mstb->tx_slots[0]) { - mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT; - mstb->tx_slots[0] = NULL; - wake_tx = true; - } - if (mstb->tx_slots[1]) { - mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT; - mstb->tx_slots[1] = NULL; + list_for_each_entry_safe(txmsg, txmsg_tmp, &mgr->tx_msg_downq, next) { + if (txmsg->dst != mstb) + continue; + + txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT; + list_del(&txmsg->next); wake_tx = true; } mutex_unlock(&mstb->mgr->qlock); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 96bcf33c03d3..9e1ffcd7cb68 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -194,11 +194,8 @@ struct drm_dp_sideband_msg_rx { * @rad: Relative Address to talk to this branch device. * @lct: Link count total to talk to this branch device. * @num_ports: number of ports on the branch. - * @msg_slots: one bit per transmitted msg slot. * @port_parent: pointer to the port parent, NULL if toplevel. * @mgr: topology manager for this branch device. - * @tx_slots: transmission slots for this device. - * @last_seqno: last sequence number used to talk to this. * @link_address_sent: if a link address message has been sent to this device yet. * @guid: guid for DP 1.2 branch device. port under this branch can be * identified by port #. @@ -239,7 +236,6 @@ struct drm_dp_mst_branch { u8 lct; int num_ports; - int msg_slots; /** * @ports: the list of ports on this branch device. This should be * considered protected for reading by &drm_dp_mst_topology_mgr.lock. @@ -252,20 +248,11 @@ struct drm_dp_mst_branch { */ struct list_head ports; - /* list of tx ops queue for this port */ struct drm_dp_mst_port *port_parent; struct drm_dp_mst_topology_mgr *mgr; - /* slots are protected by mstb->mgr->qlock */ - struct drm_dp_sideband_msg_tx *tx_slots[2]; - int last_seqno; bool link_address_sent; - /** - * @down_rep_recv: Message receiver state for down replies. - */ - struct drm_dp_sideband_msg_rx down_rep_recv[2]; - /* global unique identifier to identify branch devices */ u8 guid[16]; }; @@ -567,6 +554,12 @@ struct drm_dp_mst_topology_mgr { */ struct drm_dp_sideband_msg_rx up_req_recv; + /** + * @down_rep_recv: Message receiver state for replies to down + * requests. + */ + struct drm_dp_sideband_msg_rx down_rep_recv; + /** * @lock: protects @mst_state, @mst_primary, @dpcd, and * @payload_id_table_cleared. @@ -592,11 +585,6 @@ struct drm_dp_mst_topology_mgr { */ bool payload_id_table_cleared : 1; - /** - * @is_waiting_for_dwn_reply: whether we're waiting for a down reply. - */ - bool is_waiting_for_dwn_reply : 1; - /** * @mst_primary: Pointer to the primary/first branch device. */ @@ -621,13 +609,12 @@ struct drm_dp_mst_topology_mgr { const struct drm_private_state_funcs *funcs; /** - * @qlock: protects @tx_msg_downq, the &drm_dp_mst_branch.txslost and - * &drm_dp_sideband_msg_tx.state once they are queued + * @qlock: protects @tx_msg_downq and &drm_dp_sideband_msg_tx.state */ struct mutex qlock; /** - * @tx_msg_downq: List of pending down replies. + * @tx_msg_downq: List of pending down requests */ struct list_head tx_msg_downq; -- cgit v1.2.3 From b0b5849e0cc0195604c0945c9adb7c2570621a51 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 15 Apr 2020 09:39:36 +0200 Subject: drm: Add devm_drm_dev_alloc macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new macro helper to combine the usual init sequence in drivers, consisting of a kzalloc + devm_drm_dev_init + drmm_add_final_kfree triplet. This allows us to remove the rather unsightly drmm_add_final_kfree from all currently merged drivers. The kerneldoc is only added for this new function. Existing kerneldoc and examples will be udated at the very end, since once all drivers are converted over to devm_drm_dev_alloc we can unexport a lot of interim functions and make the documentation for driver authors a lot cleaner and less confusing. There will be only one true way to initialize a drm_device at the end of this, which is going to be devm_drm_dev_alloc. v2: - Actually explain what this is for in the commit message (Sam) - Fix checkpatch issues (Sam) Acked-by: Noralf Trønnes Cc: Noralf Trønnes Reviewed-by: Thomas Zimmermann Reviewed-by: Sam Ravnborg Cc: Sam Ravnborg Cc: Paul Kocialkowski Cc: Laurent Pinchart Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200415074034.175360-2-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_drv.c | 23 +++++++++++++++++++++++ include/drm/drm_drv.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index c15c9b4540e1..bc38322f306e 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -739,6 +739,29 @@ int devm_drm_dev_init(struct device *parent, } EXPORT_SYMBOL(devm_drm_dev_init); +void *__devm_drm_dev_alloc(struct device *parent, struct drm_driver *driver, + size_t size, size_t offset) +{ + void *container; + struct drm_device *drm; + int ret; + + container = kzalloc(size, GFP_KERNEL); + if (!container) + return ERR_PTR(-ENOMEM); + + drm = container + offset; + ret = devm_drm_dev_init(parent, drm, driver); + if (ret) { + kfree(container); + return ERR_PTR(ret); + } + drmm_add_final_kfree(drm, container); + + return container; +} +EXPORT_SYMBOL(__devm_drm_dev_alloc); + /** * drm_dev_alloc - Allocate new DRM device * @driver: DRM driver to allocate device for diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index e0ea577559ff..6d457652f199 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -623,6 +623,39 @@ int devm_drm_dev_init(struct device *parent, struct drm_device *dev, struct drm_driver *driver); +void *__devm_drm_dev_alloc(struct device *parent, struct drm_driver *driver, + size_t size, size_t offset); + +/** + * devm_drm_dev_alloc - Resource managed allocation of a &drm_device instance + * @parent: Parent device object + * @driver: DRM driver + * @type: the type of the struct which contains struct &drm_device + * @member: the name of the &drm_device within @type. + * + * This allocates and initialize a new DRM device. No device registration is done. + * Call drm_dev_register() to advertice the device to user space and register it + * with other core subsystems. This should be done last in the device + * initialization sequence to make sure userspace can't access an inconsistent + * state. + * + * The initial ref-count of the object is 1. Use drm_dev_get() and + * drm_dev_put() to take and drop further ref-counts. + * + * It is recommended that drivers embed &struct drm_device into their own device + * structure. + * + * Note that this manages the lifetime of the resulting &drm_device + * automatically using devres. The DRM device initialized with this function is + * automatically put on driver detach using drm_dev_put(). + * + * RETURNS: + * Pointer to new DRM device, or ERR_PTR on failure. + */ +#define devm_drm_dev_alloc(parent, driver, type, member) \ + ((type *) __devm_drm_dev_alloc(parent, driver, sizeof(type), \ + offsetof(type, member))) + struct drm_device *drm_dev_alloc(struct drm_driver *driver, struct device *parent); int drm_dev_register(struct drm_device *dev, unsigned long flags); -- cgit v1.2.3 From 58911c240783e0d1e7d457832416eb3347b8abbb Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 28 Apr 2020 20:19:25 +0300 Subject: drm: Nuke mode->hsync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's just calculate the hsync rate on demand. No point in wasting space storing it and risking the cached value getting out of sync with reality. v2: Move drm_mode_hsync() next to its only users Drop the TODO Reviewed-by: Sam Ravnborg Reviewed-by: Emil Velikov #v1 Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20200428171940.19552-2-ville.syrjala@linux.intel.com --- Documentation/gpu/todo.rst | 12 ------------ drivers/gpu/drm/drm_edid.c | 8 ++++++++ drivers/gpu/drm/drm_modes.c | 26 -------------------------- drivers/gpu/drm/i915/display/intel_display.c | 1 - include/drm/drm_modes.h | 11 ----------- 5 files changed, 8 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 439656f55c5d..658b52f7ffc6 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -347,18 +347,6 @@ Contact: Sean Paul Level: Starter -Remove drm_display_mode.hsync ------------------------------ - -We have drm_mode_hsync() to calculate this from hsync_start/end, since drivers -shouldn't/don't use this, remove this member to avoid any temptations to use it -in the future. If there is any debug code using drm_display_mode.hsync, convert -it to use drm_mode_hsync() instead. - -Contact: Sean Paul - -Level: Starter - connector register/unregister fixes ----------------------------------- diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 43b6ca364daa..3bd95c4b02eb 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2380,6 +2380,14 @@ bad_std_timing(u8 a, u8 b) (a == 0x20 && b == 0x20); } +static int drm_mode_hsync(const struct drm_display_mode *mode) +{ + if (mode->htotal <= 0) + return 0; + + return DIV_ROUND_CLOSEST(mode->clock, mode->htotal); +} + /** * drm_mode_std - convert standard mode info (width, height, refresh) into mode * @connector: connector of for the EDID block diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index d4d64518e11b..fec1c33b3045 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -747,32 +747,6 @@ void drm_mode_set_name(struct drm_display_mode *mode) } EXPORT_SYMBOL(drm_mode_set_name); -/** - * drm_mode_hsync - get the hsync of a mode - * @mode: mode - * - * Returns: - * @modes's hsync rate in kHz, rounded to the nearest integer. Calculates the - * value first if it is not yet set. - */ -int drm_mode_hsync(const struct drm_display_mode *mode) -{ - unsigned int calc_val; - - if (mode->hsync) - return mode->hsync; - - if (mode->htotal <= 0) - return 0; - - calc_val = (mode->clock * 1000) / mode->htotal; /* hsync in Hz */ - calc_val += 500; /* round to 1000Hz */ - calc_val /= 1000; /* truncate to kHz */ - - return calc_val; -} -EXPORT_SYMBOL(drm_mode_hsync); - /** * drm_mode_vrefresh - get the vrefresh of a mode * @mode: mode diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 346846609f45..ec7e943fd877 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -8891,7 +8891,6 @@ void intel_mode_from_pipe_config(struct drm_display_mode *mode, mode->clock = pipe_config->hw.adjusted_mode.crtc_clock; - mode->hsync = drm_mode_hsync(mode); mode->vrefresh = drm_mode_vrefresh(mode); drm_mode_set_name(mode); } diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index 99134d4f35eb..730fc31de4fb 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -390,16 +390,6 @@ struct drm_display_mode { */ int vrefresh; - /** - * @hsync: - * - * Horizontal refresh rate, for debug output in human readable form. Not - * used in a functional way. - * - * This value is in kHz. - */ - int hsync; - /** * @picture_aspect_ratio: * @@ -493,7 +483,6 @@ int of_get_drm_display_mode(struct device_node *np, int index); void drm_mode_set_name(struct drm_display_mode *mode); -int drm_mode_hsync(const struct drm_display_mode *mode); int drm_mode_vrefresh(const struct drm_display_mode *mode); void drm_mode_get_hv_timing(const struct drm_display_mode *mode, int *hdisplay, int *vdisplay); -- cgit v1.2.3 From 7837300c250cdda06bf82177fa4f1a512d290ee0 Mon Sep 17 00:00:00 2001 From: Rodrigo Siqueira Date: Wed, 29 Apr 2020 14:41:42 -0400 Subject: drm: Correct DP DSC macro typo In the file drm_dp_helper.h we have a macro named DP_DSC_THROUGHPUT_MODE_{0,1}_UPSUPPORTED, the correct name should be DP_DSC_THROUGHPUT_MODE_{0,1}_UNSUPPORTED. This commits adjusts this typo in the header file and in other places that attempt to access this macro. Reviewed-by: Harry Wentland Signed-off-by: Rodrigo Siqueira Signed-off-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/20200429184142.1867987-1-Rodrigo.Siqueira@amd.com --- drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c | 2 +- include/drm/drm_dp_helper.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c index 87d682d25278..0ea6662a1563 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c @@ -129,7 +129,7 @@ static bool dsc_line_buff_depth_from_dpcd(int dpcd_line_buff_bit_depth, int *lin static bool dsc_throughput_from_dpcd(int dpcd_throughput, int *throughput) { switch (dpcd_throughput) { - case DP_DSC_THROUGHPUT_MODE_0_UPSUPPORTED: + case DP_DSC_THROUGHPUT_MODE_0_UNSUPPORTED: *throughput = 0; break; case DP_DSC_THROUGHPUT_MODE_0_170: diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index e22cf5b2f174..09e674c228b9 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -292,7 +292,7 @@ #define DP_DSC_PEAK_THROUGHPUT 0x06B # define DP_DSC_THROUGHPUT_MODE_0_MASK (0xf << 0) # define DP_DSC_THROUGHPUT_MODE_0_SHIFT 0 -# define DP_DSC_THROUGHPUT_MODE_0_UPSUPPORTED 0 +# define DP_DSC_THROUGHPUT_MODE_0_UNSUPPORTED 0 # define DP_DSC_THROUGHPUT_MODE_0_340 (1 << 0) # define DP_DSC_THROUGHPUT_MODE_0_400 (2 << 0) # define DP_DSC_THROUGHPUT_MODE_0_450 (3 << 0) @@ -310,7 +310,7 @@ # define DP_DSC_THROUGHPUT_MODE_0_170 (15 << 0) /* 1.4a */ # define DP_DSC_THROUGHPUT_MODE_1_MASK (0xf << 4) # define DP_DSC_THROUGHPUT_MODE_1_SHIFT 4 -# define DP_DSC_THROUGHPUT_MODE_1_UPSUPPORTED 0 +# define DP_DSC_THROUGHPUT_MODE_1_UNSUPPORTED 0 # define DP_DSC_THROUGHPUT_MODE_1_340 (1 << 4) # define DP_DSC_THROUGHPUT_MODE_1_400 (2 << 4) # define DP_DSC_THROUGHPUT_MODE_1_450 (3 << 4) -- cgit v1.2.3 From ca96088aa0de3400048093adb2fa13eb10569023 Mon Sep 17 00:00:00 2001 From: Emmanuel Vadot Date: Thu, 30 Apr 2020 17:33:46 +0200 Subject: drm/client: Dual licence the header in GPL-2 and MIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Source file was dual licenced but the header was omitted, fix that. Contributors for this file are: Daniel Vetter Matt Roper Maxime Ripard Noralf Trønnes Thomas Zimmermann Acked-by: Noralf Trønnes Acked-by: Matt Roper Acked-by: Daniel Vetter Acked-by: Maxime Ripard Acked-by: Thomas Zimmermann Signed-off-by: Emmanuel Vadot Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200430153347.85323-1-manu@FreeBSD.org --- include/drm/drm_client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 7402f852d3c4..eb259c2547af 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ #ifndef _DRM_CLIENT_H_ #define _DRM_CLIENT_H_ -- cgit v1.2.3 From b7301fd812a3b103df422826c830dc9a979b2908 Mon Sep 17 00:00:00 2001 From: Maya Rashish Date: Wed, 8 Apr 2020 22:14:42 +0000 Subject: drm/ttm: Remove reference to the mem_glob member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was removed in: Author: Christian König Date: Wed Sep 25 11:38:50 2019 +0200 drm/ttm: remove pointers to globals Signed-off-by: Maya Rashish Reviewed-by: Christian König Link: https://patchwork.freedesktop.org/patch/360750/ Signed-off-by: Christian König --- include/drm/ttm/ttm_bo_driver.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index c9e0fd09f4b2..54a527aa79cc 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -390,7 +390,6 @@ struct ttm_bo_driver { /** * struct ttm_bo_global - Buffer object driver global data. * - * @mem_glob: Pointer to a struct ttm_mem_global object for accounting. * @dummy_read_page: Pointer to a dummy page used for mapping requests * of unpopulated pages. * @shrink: A shrink callback object used for buffer object swap. -- cgit v1.2.3 From 0cdea4455acd350a7f62406478e3d6d1f764cef9 Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Mon, 4 May 2020 17:40:35 +0200 Subject: drm/mm: optimize rb_hole_addr rbtree search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Userspace can severely fragment rb_hole_addr rbtree by manipulating alignment while allocating buffers. Fragmented rb_hole_addr rbtree would result in large delays while allocating buffer object for a userspace application. It takes long time to find suitable hole because if we fail to find a suitable hole in the first attempt then we look for neighbouring nodes using rb_prev()/rb_next(). Traversing rbtree using rb_prev()/rb_next() can take really long time if the tree is fragmented. This patch improves searches in fragmented rb_hole_addr rbtree by modifying it to an augmented rbtree which will store an extra field in drm_mm_node, subtree_max_hole. Each drm_mm_node now stores maximum hole size for its subtree in drm_mm_node->subtree_max_hole. Using drm_mm_node->subtree_max_hole, it is possible to eliminate a complete subtree if that subtree is unable to serve a request hence reducing number of rb_prev()/rb_next() used. With this patch applied, 1 million bo allocs on amdgpu took ~8 sec, compared to 50k bo allocs which took 28 sec without it. partial test code: int test_fragmentation(void) { int i = 0; uint32_t minor_version; uint32_t major_version; struct amdgpu_bo_alloc_request request = {}; amdgpu_bo_handle vram_handle[MAX_ALLOC] = {}; amdgpu_device_handle device_handle; request.alloc_size = 4096; request.phys_alignment = 8192; request.preferred_heap = AMDGPU_GEM_DOMAIN_VRAM; int fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); amdgpu_device_initialize(fd, &major_version, &minor_version, &device_handle); for (i = 0; i < MAX_ALLOC; i++) { amdgpu_bo_alloc(device_handle, &request, &vram_handle[i]); } for (i = 0; i < MAX_ALLOC; i++) amdgpu_bo_free(vram_handle[i]); return 0; } v2: Use RB_DECLARE_CALLBACKS_MAX to maintain subtree_max_hole v3: insert_hole_addr() should be static a function fix return value of next_hole_high_addr()/next_hole_low_addr() Reported-by: kbuild test robot v4: Fix commit message. Signed-off-by: Nirmoy Das Reviewed-by: Chris Wilson Acked-by: Christian König Link: https://patchwork.freedesktop.org/patch/364341/ Signed-off-by: Christian König --- drivers/gpu/drm/drm_mm.c | 133 ++++++++++++++++++++++++++++++++++++++++------- include/drm/drm_mm.h | 1 + 2 files changed, 115 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 8981abe8b7c9..f4ca1ff80af9 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -212,20 +212,6 @@ static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, &drm_mm_interval_tree_augment); } -#define RB_INSERT(root, member, expr) do { \ - struct rb_node **link = &root.rb_node, *rb = NULL; \ - u64 x = expr(node); \ - while (*link) { \ - rb = *link; \ - if (x < expr(rb_entry(rb, struct drm_mm_node, member))) \ - link = &rb->rb_left; \ - else \ - link = &rb->rb_right; \ - } \ - rb_link_node(&node->member, rb, link); \ - rb_insert_color(&node->member, &root); \ -} while (0) - #define HOLE_SIZE(NODE) ((NODE)->hole_size) #define HOLE_ADDR(NODE) (__drm_mm_hole_node_start(NODE)) @@ -255,16 +241,42 @@ static void insert_hole_size(struct rb_root_cached *root, rb_insert_color_cached(&node->rb_hole_size, root, first); } +RB_DECLARE_CALLBACKS_MAX(static, augment_callbacks, + struct drm_mm_node, rb_hole_addr, + u64, subtree_max_hole, HOLE_SIZE) + +static void insert_hole_addr(struct rb_root *root, struct drm_mm_node *node) +{ + struct rb_node **link = &root->rb_node, *rb_parent = NULL; + u64 start = HOLE_ADDR(node), subtree_max_hole = node->subtree_max_hole; + struct drm_mm_node *parent; + + while (*link) { + rb_parent = *link; + parent = rb_entry(rb_parent, struct drm_mm_node, rb_hole_addr); + if (parent->subtree_max_hole < subtree_max_hole) + parent->subtree_max_hole = subtree_max_hole; + if (start < HOLE_ADDR(parent)) + link = &parent->rb_hole_addr.rb_left; + else + link = &parent->rb_hole_addr.rb_right; + } + + rb_link_node(&node->rb_hole_addr, rb_parent, link); + rb_insert_augmented(&node->rb_hole_addr, root, &augment_callbacks); +} + static void add_hole(struct drm_mm_node *node) { struct drm_mm *mm = node->mm; node->hole_size = __drm_mm_hole_node_end(node) - __drm_mm_hole_node_start(node); + node->subtree_max_hole = node->hole_size; DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); insert_hole_size(&mm->holes_size, node); - RB_INSERT(mm->holes_addr, rb_hole_addr, HOLE_ADDR); + insert_hole_addr(&mm->holes_addr, node); list_add(&node->hole_stack, &mm->hole_stack); } @@ -275,8 +287,10 @@ static void rm_hole(struct drm_mm_node *node) list_del(&node->hole_stack); rb_erase_cached(&node->rb_hole_size, &node->mm->holes_size); - rb_erase(&node->rb_hole_addr, &node->mm->holes_addr); + rb_erase_augmented(&node->rb_hole_addr, &node->mm->holes_addr, + &augment_callbacks); node->hole_size = 0; + node->subtree_max_hole = 0; DRM_MM_BUG_ON(drm_mm_hole_follows(node)); } @@ -361,9 +375,90 @@ first_hole(struct drm_mm *mm, } } +/** + * next_hole_high_addr - returns next hole for a DRM_MM_INSERT_HIGH mode request + * @entry: previously selected drm_mm_node + * @size: size of the a hole needed for the request + * + * This function will verify whether left subtree of @entry has hole big enough + * to fit the requtested size. If so, it will return previous node of @entry or + * else it will return parent node of @entry + * + * It will also skip the complete left subtree if subtree_max_hole of that + * subtree is same as the subtree_max_hole of the @entry. + * + * Returns: + * previous node of @entry if left subtree of @entry can serve the request or + * else return parent of @entry + */ +static struct drm_mm_node * +next_hole_high_addr(struct drm_mm_node *entry, u64 size) +{ + struct rb_node *rb_node, *left_rb_node, *parent_rb_node; + struct drm_mm_node *left_node; + + if (!entry) + return NULL; + + rb_node = &entry->rb_hole_addr; + if (rb_node->rb_left) { + left_rb_node = rb_node->rb_left; + parent_rb_node = rb_parent(rb_node); + left_node = rb_entry(left_rb_node, + struct drm_mm_node, rb_hole_addr); + if ((left_node->subtree_max_hole < size || + entry->size == entry->subtree_max_hole) && + parent_rb_node && parent_rb_node->rb_left != rb_node) + return rb_hole_addr_to_node(parent_rb_node); + } + + return rb_hole_addr_to_node(rb_prev(rb_node)); +} + +/** + * next_hole_low_addr - returns next hole for a DRM_MM_INSERT_LOW mode request + * @entry: previously selected drm_mm_node + * @size: size of the a hole needed for the request + * + * This function will verify whether right subtree of @entry has hole big enough + * to fit the requtested size. If so, it will return next node of @entry or + * else it will return parent node of @entry + * + * It will also skip the complete right subtree if subtree_max_hole of that + * subtree is same as the subtree_max_hole of the @entry. + * + * Returns: + * next node of @entry if right subtree of @entry can serve the request or + * else return parent of @entry + */ +static struct drm_mm_node * +next_hole_low_addr(struct drm_mm_node *entry, u64 size) +{ + struct rb_node *rb_node, *right_rb_node, *parent_rb_node; + struct drm_mm_node *right_node; + + if (!entry) + return NULL; + + rb_node = &entry->rb_hole_addr; + if (rb_node->rb_right) { + right_rb_node = rb_node->rb_right; + parent_rb_node = rb_parent(rb_node); + right_node = rb_entry(right_rb_node, + struct drm_mm_node, rb_hole_addr); + if ((right_node->subtree_max_hole < size || + entry->size == entry->subtree_max_hole) && + parent_rb_node && parent_rb_node->rb_right != rb_node) + return rb_hole_addr_to_node(parent_rb_node); + } + + return rb_hole_addr_to_node(rb_next(rb_node)); +} + static struct drm_mm_node * next_hole(struct drm_mm *mm, struct drm_mm_node *node, + u64 size, enum drm_mm_insert_mode mode) { switch (mode) { @@ -372,10 +467,10 @@ next_hole(struct drm_mm *mm, return rb_hole_size_to_node(rb_prev(&node->rb_hole_size)); case DRM_MM_INSERT_LOW: - return rb_hole_addr_to_node(rb_next(&node->rb_hole_addr)); + return next_hole_low_addr(node, size); case DRM_MM_INSERT_HIGH: - return rb_hole_addr_to_node(rb_prev(&node->rb_hole_addr)); + return next_hole_high_addr(node, size); case DRM_MM_INSERT_EVICT: node = list_next_entry(node, hole_stack); @@ -489,7 +584,7 @@ int drm_mm_insert_node_in_range(struct drm_mm * const mm, remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; for (hole = first_hole(mm, range_start, range_end, size, mode); hole; - hole = once ? NULL : next_hole(mm, hole, mode)) { + hole = once ? NULL : next_hole(mm, hole, size, mode)) { u64 hole_start = __drm_mm_hole_node_start(hole); u64 hole_end = hole_start + hole->hole_size; u64 adj_start, adj_end; diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index ee8b0e80ca90..a01bc6fac83c 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -168,6 +168,7 @@ struct drm_mm_node { struct rb_node rb_hole_addr; u64 __subtree_last; u64 hole_size; + u64 subtree_max_hole; unsigned long flags; #define DRM_MM_NODE_ALLOCATED_BIT 0 #define DRM_MM_NODE_SCANNED_BIT 1 -- cgit v1.2.3