Commit 5fff80bb authored by Maarten Lankhorst's avatar Maarten Lankhorst Committed by Dave Airlie

drm/atomic: Allow for holes in connector state, v2.

Because we record connector_mask using 1 << drm_connector_index now
the connector_mask should stay the same even when other connectors
are removed. This was not the case with MST, in that case when removing
a connector all other connectors may change their index.

This is fixed by waiting until the first get_connector_state to allocate
connector_state, and force reallocation when state is too small.

As a side effect connector arrays no longer have to be preallocated,
and can be allocated on first use which means a less allocations in
the page flip only path.

Changes since v1:
- Whitespace. (Ville)
- Call ida_remove when destroying the connector. (Ville)
- u32 alloc -> int. (Ville)

Fixes: 14de6c44 ("drm/atomic: Remove drm_atomic_connectors_for_crtc.")
Signed-off-by: default avatarMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: default avatarLyude <cpaul@redhat.com>
Reviewed-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 5441ea11
...@@ -65,8 +65,6 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) ...@@ -65,8 +65,6 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
*/ */
state->allow_modeset = true; state->allow_modeset = true;
state->num_connector = ACCESS_ONCE(dev->mode_config.num_connector);
state->crtcs = kcalloc(dev->mode_config.num_crtc, state->crtcs = kcalloc(dev->mode_config.num_crtc,
sizeof(*state->crtcs), GFP_KERNEL); sizeof(*state->crtcs), GFP_KERNEL);
if (!state->crtcs) if (!state->crtcs)
...@@ -83,16 +81,6 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) ...@@ -83,16 +81,6 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
sizeof(*state->plane_states), GFP_KERNEL); sizeof(*state->plane_states), GFP_KERNEL);
if (!state->plane_states) if (!state->plane_states)
goto fail; goto fail;
state->connectors = kcalloc(state->num_connector,
sizeof(*state->connectors),
GFP_KERNEL);
if (!state->connectors)
goto fail;
state->connector_states = kcalloc(state->num_connector,
sizeof(*state->connector_states),
GFP_KERNEL);
if (!state->connector_states)
goto fail;
state->dev = dev; state->dev = dev;
...@@ -823,19 +811,27 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, ...@@ -823,19 +811,27 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
index = drm_connector_index(connector); index = drm_connector_index(connector);
/*
* Construction of atomic state updates can race with a connector
* hot-add which might overflow. In this case flip the table and just
* restart the entire ioctl - no one is fast enough to livelock a cpu
* with physical hotplug events anyway.
*
* Note that we only grab the indexes once we have the right lock to
* prevent hotplug/unplugging of connectors. So removal is no problem,
* at most the array is a bit too large.
*/
if (index >= state->num_connector) { if (index >= state->num_connector) {
DRM_DEBUG_ATOMIC("Hot-added connector would overflow state array, restarting\n"); struct drm_connector **c;
return ERR_PTR(-EAGAIN); struct drm_connector_state **cs;
int alloc = max(index + 1, config->num_connector);
c = krealloc(state->connectors, alloc * sizeof(*state->connectors), GFP_KERNEL);
if (!c)
return ERR_PTR(-ENOMEM);
state->connectors = c;
memset(&state->connectors[state->num_connector], 0,
sizeof(*state->connectors) * (alloc - state->num_connector));
cs = krealloc(state->connector_states, alloc * sizeof(*state->connector_states), GFP_KERNEL);
if (!cs)
return ERR_PTR(-ENOMEM);
state->connector_states = cs;
memset(&state->connector_states[state->num_connector], 0,
sizeof(*state->connector_states) * (alloc - state->num_connector));
state->num_connector = alloc;
} }
if (state->connector_states[index]) if (state->connector_states[index])
......
...@@ -1493,7 +1493,7 @@ void drm_atomic_helper_swap_state(struct drm_device *dev, ...@@ -1493,7 +1493,7 @@ void drm_atomic_helper_swap_state(struct drm_device *dev,
{ {
int i; int i;
for (i = 0; i < dev->mode_config.num_connector; i++) { for (i = 0; i < state->num_connector; i++) {
struct drm_connector *connector = state->connectors[i]; struct drm_connector *connector = state->connectors[i];
if (!connector) if (!connector)
......
...@@ -918,12 +918,19 @@ int drm_connector_init(struct drm_device *dev, ...@@ -918,12 +918,19 @@ int drm_connector_init(struct drm_device *dev,
connector->base.properties = &connector->properties; connector->base.properties = &connector->properties;
connector->dev = dev; connector->dev = dev;
connector->funcs = funcs; connector->funcs = funcs;
connector->connector_id = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL);
if (connector->connector_id < 0) {
ret = connector->connector_id;
goto out_put;
}
connector->connector_type = connector_type; connector->connector_type = connector_type;
connector->connector_type_id = connector->connector_type_id =
ida_simple_get(connector_ida, 1, 0, GFP_KERNEL); ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);
if (connector->connector_type_id < 0) { if (connector->connector_type_id < 0) {
ret = connector->connector_type_id; ret = connector->connector_type_id;
goto out_put; goto out_put_id;
} }
connector->name = connector->name =
kasprintf(GFP_KERNEL, "%s-%d", kasprintf(GFP_KERNEL, "%s-%d",
...@@ -931,7 +938,7 @@ int drm_connector_init(struct drm_device *dev, ...@@ -931,7 +938,7 @@ int drm_connector_init(struct drm_device *dev,
connector->connector_type_id); connector->connector_type_id);
if (!connector->name) { if (!connector->name) {
ret = -ENOMEM; ret = -ENOMEM;
goto out_put; goto out_put_type_id;
} }
INIT_LIST_HEAD(&connector->probed_modes); INIT_LIST_HEAD(&connector->probed_modes);
...@@ -959,7 +966,12 @@ int drm_connector_init(struct drm_device *dev, ...@@ -959,7 +966,12 @@ int drm_connector_init(struct drm_device *dev,
} }
connector->debugfs_entry = NULL; connector->debugfs_entry = NULL;
out_put_type_id:
if (ret)
ida_remove(connector_ida, connector->connector_type_id);
out_put_id:
if (ret)
ida_remove(&config->connector_ida, connector->connector_id);
out_put: out_put:
if (ret) if (ret)
drm_mode_object_put(dev, &connector->base); drm_mode_object_put(dev, &connector->base);
...@@ -996,6 +1008,9 @@ void drm_connector_cleanup(struct drm_connector *connector) ...@@ -996,6 +1008,9 @@ void drm_connector_cleanup(struct drm_connector *connector)
ida_remove(&drm_connector_enum_list[connector->connector_type].ida, ida_remove(&drm_connector_enum_list[connector->connector_type].ida,
connector->connector_type_id); connector->connector_type_id);
ida_remove(&dev->mode_config.connector_ida,
connector->connector_id);
kfree(connector->display_info.bus_formats); kfree(connector->display_info.bus_formats);
drm_mode_object_put(dev, &connector->base); drm_mode_object_put(dev, &connector->base);
kfree(connector->name); kfree(connector->name);
...@@ -1012,32 +1027,6 @@ void drm_connector_cleanup(struct drm_connector *connector) ...@@ -1012,32 +1027,6 @@ void drm_connector_cleanup(struct drm_connector *connector)
} }
EXPORT_SYMBOL(drm_connector_cleanup); EXPORT_SYMBOL(drm_connector_cleanup);
/**
* drm_connector_index - find the index of a registered connector
* @connector: connector to find index for
*
* Given a registered connector, return the index of that connector within a DRM
* device's list of connectors.
*/
unsigned int drm_connector_index(struct drm_connector *connector)
{
unsigned int index = 0;
struct drm_connector *tmp;
struct drm_mode_config *config = &connector->dev->mode_config;
WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
drm_for_each_connector(tmp, connector->dev) {
if (tmp == connector)
return index;
index++;
}
BUG();
}
EXPORT_SYMBOL(drm_connector_index);
/** /**
* drm_connector_register - register a connector * drm_connector_register - register a connector
* @connector: the connector to register * @connector: the connector to register
...@@ -5789,6 +5778,7 @@ void drm_mode_config_init(struct drm_device *dev) ...@@ -5789,6 +5778,7 @@ void drm_mode_config_init(struct drm_device *dev)
INIT_LIST_HEAD(&dev->mode_config.plane_list); INIT_LIST_HEAD(&dev->mode_config.plane_list);
idr_init(&dev->mode_config.crtc_idr); idr_init(&dev->mode_config.crtc_idr);
idr_init(&dev->mode_config.tile_idr); idr_init(&dev->mode_config.tile_idr);
ida_init(&dev->mode_config.connector_ida);
drm_modeset_lock_all(dev); drm_modeset_lock_all(dev);
drm_mode_create_standard_properties(dev); drm_mode_create_standard_properties(dev);
...@@ -5869,6 +5859,7 @@ void drm_mode_config_cleanup(struct drm_device *dev) ...@@ -5869,6 +5859,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
crtc->funcs->destroy(crtc); crtc->funcs->destroy(crtc);
} }
ida_destroy(&dev->mode_config.connector_ida);
idr_destroy(&dev->mode_config.tile_idr); idr_destroy(&dev->mode_config.tile_idr);
idr_destroy(&dev->mode_config.crtc_idr); idr_destroy(&dev->mode_config.crtc_idr);
drm_modeset_lock_fini(&dev->mode_config.connection_mutex); drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
......
...@@ -1166,6 +1166,7 @@ struct drm_connector { ...@@ -1166,6 +1166,7 @@ struct drm_connector {
struct drm_mode_object base; struct drm_mode_object base;
char *name; char *name;
int connector_id;
int connector_type; int connector_type;
int connector_type_id; int connector_type_id;
bool interlace_allowed; bool interlace_allowed;
...@@ -2047,6 +2048,7 @@ struct drm_mode_config { ...@@ -2047,6 +2048,7 @@ struct drm_mode_config {
struct list_head fb_list; struct list_head fb_list;
int num_connector; int num_connector;
struct ida connector_ida;
struct list_head connector_list; struct list_head connector_list;
int num_encoder; int num_encoder;
struct list_head encoder_list; struct list_head encoder_list;
...@@ -2200,7 +2202,11 @@ int drm_connector_register(struct drm_connector *connector); ...@@ -2200,7 +2202,11 @@ int drm_connector_register(struct drm_connector *connector);
void drm_connector_unregister(struct drm_connector *connector); void drm_connector_unregister(struct drm_connector *connector);
extern void drm_connector_cleanup(struct drm_connector *connector); extern void drm_connector_cleanup(struct drm_connector *connector);
extern unsigned int drm_connector_index(struct drm_connector *connector); static inline unsigned drm_connector_index(struct drm_connector *connector)
{
return connector->connector_id;
}
/* helper to unplug all connectors from sysfs for device */ /* helper to unplug all connectors from sysfs for device */
extern void drm_connector_unplug_all(struct drm_device *dev); extern void drm_connector_unplug_all(struct drm_device *dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment