提交 49f89764 编写于 作者: D Dave Airlie

Merge tag 'topic/drm-misc-2015-05-06' of git://anongit.freedesktop.org/drm-intel into drm-next

misc drm core patches.

* tag 'topic/drm-misc-2015-05-06' of git://anongit.freedesktop.org/drm-intel:
  drm: simplify master cleanup
  drm: simplify authentication management
  drm: drop unused 'magicfree' list
  drm: fix a memleak on mutex failure path
  drm/atomic-helper: Really recover pre-atomic plane/cursor behavior
  drm/qxl: Fix qxl_noop_get_vblank_counter()
  drm: Zero out invalid vblank timestamp in drm_update_vblank_count. (v2)
  drm: Prevent invalid use of vblank_disable_immediate. (v2)
  drm/vblank: Fixup and document timestamp update/read barriers
  DRM: Don't re-poll connector for disconnect
  drm: Fix for DP CTS test 4.2.2.5 - I2C DEFER handling
  drm: Fix the 'native defer' message in drm_dp_i2c_do_msg()
  drm/atomic-helper: Don't call atomic_update_plane when it stays off
......@@ -1138,7 +1138,8 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
if (drm_atomic_plane_disabling(plane, old_plane_state) &&
funcs->atomic_disable)
funcs->atomic_disable(plane, old_plane_state);
else
else if (plane->state->crtc ||
drm_atomic_plane_disabling(plane, old_plane_state))
funcs->atomic_update(plane, old_plane_state);
}
......@@ -1309,13 +1310,13 @@ int drm_atomic_helper_update_plane(struct drm_plane *plane,
plane_state->src_h = src_h;
plane_state->src_w = src_w;
if (plane == crtc->cursor)
state->legacy_cursor_update = true;
ret = drm_atomic_commit(state);
if (ret != 0)
goto fail;
if (plane == crtc->cursor)
state->legacy_cursor_update = true;
/* Driver takes ownership of state on successful commit. */
return 0;
fail:
......
/**
* \file drm_auth.c
* IOCTLs for authentication
*
* \author Rickard E. (Rik) Faith <faith@valinux.com>
* \author Gareth Hughes <gareth@valinux.com>
*/
/*
* Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com
*
......@@ -13,6 +5,9 @@
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Author Rickard E. (Rik) Faith <faith@valinux.com>
* Author Gareth Hughes <gareth@valinux.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
......@@ -36,154 +31,47 @@
#include <drm/drmP.h>
#include "drm_internal.h"
struct drm_magic_entry {
struct list_head head;
struct drm_hash_item hash_item;
struct drm_file *priv;
};
/**
* Find the file with the given magic number.
*
* \param dev DRM device.
* \param magic magic number.
*
* Searches in drm_device::magiclist within all files with the same hash key
* the one with matching magic number, while holding the drm_device::struct_mutex
* lock.
*/
static struct drm_file *drm_find_file(struct drm_master *master, drm_magic_t magic)
{
struct drm_file *retval = NULL;
struct drm_magic_entry *pt;
struct drm_hash_item *hash;
struct drm_device *dev = master->minor->dev;
mutex_lock(&dev->struct_mutex);
if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
retval = pt->priv;
}
mutex_unlock(&dev->struct_mutex);
return retval;
}
/**
* Adds a magic number.
*
* \param dev DRM device.
* \param priv file private data.
* \param magic magic number.
*
* Creates a drm_magic_entry structure and appends to the linked list
* associated the magic number hash key in drm_device::magiclist, while holding
* the drm_device::struct_mutex lock.
*/
static int drm_add_magic(struct drm_master *master, struct drm_file *priv,
drm_magic_t magic)
{
struct drm_magic_entry *entry;
struct drm_device *dev = master->minor->dev;
DRM_DEBUG("%d\n", magic);
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->priv = priv;
entry->hash_item.key = (unsigned long)magic;
mutex_lock(&dev->struct_mutex);
drm_ht_insert_item(&master->magiclist, &entry->hash_item);
list_add_tail(&entry->head, &master->magicfree);
mutex_unlock(&dev->struct_mutex);
return 0;
}
/**
* Remove a magic number.
*
* \param dev DRM device.
* \param magic magic number.
*
* Searches and unlinks the entry in drm_device::magiclist with the magic
* number hash key, while holding the drm_device::struct_mutex lock.
*/
int drm_remove_magic(struct drm_master *master, drm_magic_t magic)
{
struct drm_magic_entry *pt;
struct drm_hash_item *hash;
struct drm_device *dev = master->minor->dev;
DRM_DEBUG("%d\n", magic);
mutex_lock(&dev->struct_mutex);
if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
drm_ht_remove_item(&master->magiclist, hash);
list_del(&pt->head);
mutex_unlock(&dev->struct_mutex);
kfree(pt);
return 0;
}
/**
* Get a unique magic number (ioctl).
* drm_getmagic - Get unique magic of a client
* @dev: DRM device to operate on
* @data: ioctl data containing the drm_auth object
* @file_priv: DRM file that performs the operation
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg pointer to a resulting drm_auth structure.
* \return zero on success, or a negative number on failure.
* This looks up the unique magic of the passed client and returns it. If the
* client did not have a magic assigned, yet, a new one is registered. The magic
* is stored in the passed drm_auth object.
*
* If there is a magic number in drm_file::magic then use it, otherwise
* searches an unique non-zero magic number and add it associating it with \p
* file_priv.
* This ioctl needs protection by the drm_global_mutex, which protects
* struct drm_file::magic and struct drm_magic_entry::priv.
* Returns: 0 on success, negative error code on failure.
*/
int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
static drm_magic_t sequence = 0;
static DEFINE_SPINLOCK(lock);
struct drm_auth *auth = data;
int ret = 0;
/* Find unique magic */
if (file_priv->magic) {
auth->magic = file_priv->magic;
} else {
do {
spin_lock(&lock);
if (!sequence)
++sequence; /* reserve 0 */
auth->magic = sequence++;
spin_unlock(&lock);
} while (drm_find_file(file_priv->master, auth->magic));
file_priv->magic = auth->magic;
drm_add_magic(file_priv->master, file_priv, auth->magic);
mutex_lock(&dev->struct_mutex);
if (!file_priv->magic) {
ret = idr_alloc(&file_priv->master->magic_map, file_priv,
1, 0, GFP_KERNEL);
if (ret >= 0)
file_priv->magic = ret;
}
auth->magic = file_priv->magic;
mutex_unlock(&dev->struct_mutex);
DRM_DEBUG("%u\n", auth->magic);
return 0;
return ret < 0 ? ret : 0;
}
/**
* Authenticate with a magic.
* drm_authmagic - Authenticate client with a magic
* @dev: DRM device to operate on
* @data: ioctl data containing the drm_auth object
* @file_priv: DRM file that performs the operation
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg pointer to a drm_auth structure.
* \return zero if authentication successed, or a negative number otherwise.
* This looks up a DRM client by the passed magic and authenticates it.
*
* Checks if \p file_priv is associated with the magic number passed in \arg.
* This ioctl needs protection by the drm_global_mutex, which protects
* struct drm_file::magic and struct drm_magic_entry::priv.
* Returns: 0 on success, negative error code on failure.
*/
int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file_priv)
......@@ -192,10 +80,14 @@ int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file;
DRM_DEBUG("%u\n", auth->magic);
if ((file = drm_find_file(file_priv->master, auth->magic))) {
mutex_lock(&dev->struct_mutex);
file = idr_find(&file_priv->master->magic_map, auth->magic);
if (file) {
file->authenticated = 1;
drm_remove_magic(file_priv->master, auth->magic);
return 0;
idr_replace(&file_priv->master->magic_map, NULL, auth->magic);
}
return -EINVAL;
mutex_unlock(&dev->struct_mutex);
return file ? 0 : -EINVAL;
}
......@@ -432,7 +432,7 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
*/
static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
{
unsigned int retry;
unsigned int retry, defer_i2c;
int ret;
/*
......@@ -440,7 +440,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
* is required to retry at least seven times upon receiving AUX_DEFER
* before giving up the AUX transaction.
*/
for (retry = 0; retry < 7; retry++) {
for (retry = 0, defer_i2c = 0; retry < (7 + defer_i2c); retry++) {
mutex_lock(&aux->hw_mutex);
ret = aux->transfer(aux, msg);
mutex_unlock(&aux->hw_mutex);
......@@ -466,7 +466,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
return -EREMOTEIO;
case DP_AUX_NATIVE_REPLY_DEFER:
DRM_DEBUG_KMS("native defer");
DRM_DEBUG_KMS("native defer\n");
/*
* We could check for I2C bit rate capabilities and if
* available adjust this interval. We could also be
......@@ -499,7 +499,13 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
case DP_AUX_I2C_REPLY_DEFER:
DRM_DEBUG_KMS("I2C defer\n");
/* DP Compliance Test 4.2.2.5 Requirement:
* Must have at least 7 retries for I2C defers on the
* transaction to pass this test
*/
aux->i2c_defer_count++;
if (defer_i2c < 7)
defer_i2c++;
usleep_range(400, 500);
continue;
......
......@@ -92,8 +92,6 @@ void drm_ut_debug_printk(const char *function_name, const char *format, ...)
}
EXPORT_SYMBOL(drm_ut_debug_printk);
#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */
struct drm_master *drm_master_create(struct drm_minor *minor)
{
struct drm_master *master;
......@@ -105,11 +103,7 @@ struct drm_master *drm_master_create(struct drm_minor *minor)
kref_init(&master->refcount);
spin_lock_init(&master->lock.spinlock);
init_waitqueue_head(&master->lock.lock_queue);
if (drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER)) {
kfree(master);
return NULL;
}
INIT_LIST_HEAD(&master->magicfree);
idr_init(&master->magic_map);
master->minor = minor;
return master;
......@@ -138,16 +132,10 @@ static void drm_master_destroy(struct kref *kref)
r_list = NULL;
}
}
if (master->unique) {
kfree(master->unique);
master->unique = NULL;
master->unique_len = 0;
}
drm_ht_remove(&master->magiclist);
mutex_unlock(&dev->struct_mutex);
idr_destroy(&master->magic_map);
kfree(master->unique);
kfree(master);
}
......
......@@ -380,6 +380,8 @@ int drm_release(struct inode *inode, struct file *filp)
mutex_lock(&dev->struct_mutex);
list_del(&file_priv->lhead);
if (file_priv->magic)
idr_remove(&file_priv->master->magic_map, file_priv->magic);
mutex_unlock(&dev->struct_mutex);
if (dev->driver->preclose)
......@@ -394,11 +396,6 @@ int drm_release(struct inode *inode, struct file *filp)
(long)old_encode_dev(file_priv->minor->kdev->devt),
dev->open_count);
/* Release any auth tokens that might point to this file_priv,
(do that under the drm_global_mutex) */
if (file_priv->magic)
(void) drm_remove_magic(file_priv->master, file_priv->magic);
/* if the master has gone away we can't do anything with the lock */
if (file_priv->minor->master)
drm_master_release(dev, filp);
......
......@@ -69,7 +69,6 @@ int drm_getmagic(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_remove_magic(struct drm_master *master, drm_magic_t magic);
/* drm_sysfs.c */
extern struct class *drm_class;
......
......@@ -74,6 +74,36 @@ module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
static void store_vblank(struct drm_device *dev, int crtc,
unsigned vblank_count_inc,
struct timeval *t_vblank)
{
struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
u32 tslot;
assert_spin_locked(&dev->vblank_time_lock);
if (t_vblank) {
/* All writers hold the spinlock, but readers are serialized by
* the latching of vblank->count below.
*/
tslot = vblank->count + vblank_count_inc;
vblanktimestamp(dev, crtc, tslot) = *t_vblank;
}
/*
* vblank timestamp updates are protected on the write side with
* vblank_time_lock, but on the read side done locklessly using a
* sequence-lock on the vblank counter. Ensure correct ordering using
* memory barrriers. We need the barrier both before and also after the
* counter update to synchronize with the next timestamp write.
* The read-side barriers for this are in drm_vblank_count_and_time.
*/
smp_wmb();
vblank->count += vblank_count_inc;
smp_wmb();
}
/**
* drm_update_vblank_count - update the master vblank counter
* @dev: DRM device
......@@ -93,7 +123,7 @@ module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
static void drm_update_vblank_count(struct drm_device *dev, int crtc)
{
struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
u32 cur_vblank, diff, tslot;
u32 cur_vblank, diff;
bool rc;
struct timeval t_vblank;
......@@ -129,18 +159,15 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
if (diff == 0)
return;
/* Reinitialize corresponding vblank timestamp if high-precision query
* available. Skip this step if query unsupported or failed. Will
* reinitialize delayed at next vblank interrupt in that case.
/*
* Only reinitialize corresponding vblank timestamp if high-precision query
* available and didn't fail. Otherwise reinitialize delayed at next vblank
* interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
*/
if (rc) {
tslot = atomic_read(&vblank->count) + diff;
vblanktimestamp(dev, crtc, tslot) = t_vblank;
}
if (!rc)
t_vblank = (struct timeval) {0, 0};
smp_mb__before_atomic();
atomic_add(diff, &vblank->count);
smp_mb__after_atomic();
store_vblank(dev, crtc, diff, &t_vblank);
}
/*
......@@ -218,7 +245,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
/* Compute time difference to stored timestamp of last vblank
* as updated by last invocation of drm_handle_vblank() in vblank irq.
*/
vblcount = atomic_read(&vblank->count);
vblcount = vblank->count;
diff_ns = timeval_to_ns(&tvblank) -
timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount));
......@@ -234,17 +261,8 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
* available. In that case we can't account for this and just
* hope for the best.
*/
if (vblrc && (abs64(diff_ns) > 1000000)) {
/* Store new timestamp in ringbuffer. */
vblanktimestamp(dev, crtc, vblcount + 1) = tvblank;
/* Increment cooked vblank count. This also atomically commits
* the timestamp computed above.
*/
smp_mb__before_atomic();
atomic_inc(&vblank->count);
smp_mb__after_atomic();
}
if (vblrc && (abs64(diff_ns) > 1000000))
store_vblank(dev, crtc, 1, &tvblank);
spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
}
......@@ -337,6 +355,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
else
DRM_INFO("No driver support for vblank timestamp query.\n");
/* Must have precise timestamping for reliable vblank instant disable */
if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) {
dev->vblank_disable_immediate = false;
DRM_INFO("Setting vblank_disable_immediate to false because "
"get_vblank_timestamp == NULL\n");
}
dev->vblank_disable_allowed = false;
return 0;
......@@ -852,7 +877,7 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc)
if (WARN_ON(crtc >= dev->num_crtcs))
return 0;
return atomic_read(&vblank->count);
return vblank->count;
}
EXPORT_SYMBOL(drm_vblank_count);
......@@ -897,16 +922,17 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc,
if (WARN_ON(crtc >= dev->num_crtcs))
return 0;
/* Read timestamp from slot of _vblank_time ringbuffer
* that corresponds to current vblank count. Retry if
* count has incremented during readout. This works like
* a seqlock.
/*
* Vblank timestamps are read lockless. To ensure consistency the vblank
* counter is rechecked and ordering is ensured using memory barriers.
* This works like a seqlock. The write-side barriers are in store_vblank.
*/
do {
cur_vblank = atomic_read(&vblank->count);
cur_vblank = vblank->count;
smp_rmb();
*vblanktime = vblanktimestamp(dev, crtc, cur_vblank);
smp_rmb();
} while (cur_vblank != atomic_read(&vblank->count));
} while (cur_vblank != vblank->count);
return cur_vblank;
}
......@@ -1715,7 +1741,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
*/
/* Get current timestamp and count. */
vblcount = atomic_read(&vblank->count);
vblcount = vblank->count;
drm_get_last_vbltimestamp(dev, crtc, &tvblank, DRM_CALLED_FROM_VBLIRQ);
/* Compute time difference to timestamp of last vblank */
......@@ -1731,20 +1757,11 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
* e.g., due to spurious vblank interrupts. We need to
* ignore those for accounting.
*/
if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) {
/* Store new timestamp in ringbuffer. */
vblanktimestamp(dev, crtc, vblcount + 1) = tvblank;
/* Increment cooked vblank count. This also atomically commits
* the timestamp computed above.
*/
smp_mb__before_atomic();
atomic_inc(&vblank->count);
smp_mb__after_atomic();
} else {
if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS)
store_vblank(dev, crtc, 1, &tvblank);
else
DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n",
crtc, (int) diff_ns);
}
spin_unlock(&dev->vblank_time_lock);
......
......@@ -80,8 +80,10 @@ int __drm_modeset_lock_all(struct drm_device *dev,
return -ENOMEM;
if (trylock) {
if (!mutex_trylock(&config->mutex))
return -EBUSY;
if (!mutex_trylock(&config->mutex)) {
ret = -EBUSY;
goto out;
}
} else {
mutex_lock(&config->mutex);
}
......@@ -114,6 +116,8 @@ int __drm_modeset_lock_all(struct drm_device *dev,
goto retry;
}
out:
kfree(ctx);
return ret;
}
EXPORT_SYMBOL(__drm_modeset_lock_all);
......
......@@ -323,8 +323,6 @@ static void output_poll_execute(struct work_struct *work)
if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD)
continue;
repoll = true;
old_status = connector->status;
/* if we are connected and don't want to poll for disconnect
skip it */
......@@ -332,6 +330,8 @@ static void output_poll_execute(struct work_struct *work)
!(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT))
continue;
repoll = true;
connector->status = connector->funcs->detect(connector, false);
if (old_status != connector->status) {
const char *old, *new;
......
......@@ -172,10 +172,6 @@ static void intel_plane_atomic_update(struct drm_plane *plane,
struct intel_plane_state *intel_state =
to_intel_plane_state(plane->state);
/* Don't disable an already disabled plane */
if (!plane->state->fb && !old_state->fb)
return;
intel_plane->commit_plane(plane, intel_state);
}
......
......@@ -198,7 +198,7 @@ static int qxl_pm_restore(struct device *dev)
static u32 qxl_noop_get_vblank_counter(struct drm_device *dev, int crtc)
{
return dev->vblank[crtc].count.counter;
return 0;
}
static int qxl_noop_enable_vblank(struct drm_device *dev, int crtc)
......
......@@ -355,8 +355,7 @@ struct drm_lock_data {
* @minor: Link back to minor char device we are master for. Immutable.
* @unique: Unique identifier: e.g. busid. Protected by drm_global_mutex.
* @unique_len: Length of unique field. Protected by drm_global_mutex.
* @magiclist: Hash of used authentication tokens. Protected by struct_mutex.
* @magicfree: List of used authentication tokens. Protected by struct_mutex.
* @magic_map: Map of used authentication tokens. Protected by struct_mutex.
* @lock: DRI lock information.
* @driver_priv: Pointer to driver-private information.
*/
......@@ -365,8 +364,7 @@ struct drm_master {
struct drm_minor *minor;
char *unique;
int unique_len;
struct drm_open_hash magiclist;
struct list_head magicfree;
struct idr magic_map;
struct drm_lock_data lock;
void *driver_priv;
};
......@@ -686,9 +684,13 @@ struct drm_pending_vblank_event {
struct drm_vblank_crtc {
struct drm_device *dev; /* pointer to the drm_device */
wait_queue_head_t queue; /**< VBLANK wait queue */
struct timeval time[DRM_VBLANKTIME_RBSIZE]; /**< timestamp of current count */
struct timer_list disable_timer; /* delayed disable timer */
atomic_t count; /**< number of VBLANK interrupts */
/* vblank counter, protected by dev->vblank_time_lock for writes */
unsigned long count;
/* vblank timestamps, protected by dev->vblank_time_lock for writes */
struct timeval time[DRM_VBLANKTIME_RBSIZE];
atomic_t refcount; /* number of users of vblank interruptsper crtc */
u32 last; /* protected by dev->vbl_lock, used */
/* for wraparound handling */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册