Commit 7eb5cd9c authored by dnicoara's avatar dnicoara Committed by Commit bot

[Ozone-Drm] Keep track of DRM devices in browser process

Keeping the DRM devices open in the browser process means the kernel
doesn't need to clean up the display state when the GPU process crashes,
eliminating the flicker caused by the kernel disabling the CRTCs on GPU
crashes.

BUG=445773

Review URL: https://codereview.chromium.org/1064453003

Cr-Commit-Position: refs/heads/master@{#327557}
parent 46be112a
......@@ -71,6 +71,8 @@ source_set("drm_common") {
"host/display_manager.h",
"host/drm_cursor.cc",
"host/drm_cursor.h",
"host/drm_device_handle.cc",
"host/drm_device_handle.h",
"host/drm_gpu_platform_support_host.cc",
"host/drm_gpu_platform_support_host.h",
"host/drm_native_display_delegate.cc",
......
......@@ -92,6 +92,8 @@
'host/display_manager.h',
'host/drm_cursor.cc',
'host/drm_cursor.h',
'host/drm_device_handle.cc',
'host/drm_device_handle.h',
'host/drm_gpu_platform_support_host.cc',
'host/drm_gpu_platform_support_host.h',
'host/drm_native_display_delegate.cc',
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/drm/host/drm_device_handle.h"
#include <fcntl.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "base/files/file_path.h"
#include "base/posix/eintr_wrapper.h"
#include "base/threading/thread_restrictions.h"
namespace ui {
namespace {
bool Authenticate(int fd) {
drm_magic_t magic;
memset(&magic, 0, sizeof(magic));
// We need to make sure the DRM device has enough privilege. Use the DRM
// authentication logic to figure out if the device has enough permissions.
return !drmGetMagic(fd, &magic) && !drmAuthMagic(fd, magic);
}
} // namespace
DrmDeviceHandle::DrmDeviceHandle() {
}
DrmDeviceHandle::~DrmDeviceHandle() {
base::ThreadRestrictions::AssertIOAllowed();
}
bool DrmDeviceHandle::Initialize(const base::FilePath& path) {
CHECK(path.DirName() == base::FilePath("/dev/dri"));
base::ThreadRestrictions::AssertIOAllowed();
bool print_warning = true;
while (true) {
file_.reset();
int fd = HANDLE_EINTR(open(path.value().c_str(), O_RDWR | O_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "Failed to open " << path.value();
return false;
}
file_.reset(fd);
if (Authenticate(file_.get()))
break;
LOG_IF(WARNING, print_warning) << "Failed to authenticate " << path.value();
print_warning = false;
usleep(100000);
}
VLOG(1) << "Succeeded authenticating " << path.value();
return true;
}
bool DrmDeviceHandle::IsValid() const {
return file_.is_valid();
}
base::ScopedFD DrmDeviceHandle::Duplicate() {
DCHECK(file_.is_valid());
int fd = dup(file_.get());
if (fd < 0) {
PLOG(ERROR) << "Failed to dup";
return base::ScopedFD();
}
return base::ScopedFD(fd);
}
} // namespace ui
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_OZONE_PLATFORM_DRM_HOST_DRM_DEVICE_HANDLE_H_
#define UI_OZONE_PLATFORM_DRM_HOST_DRM_DEVICE_HANDLE_H_
#include "base/files/scoped_file.h"
namespace base {
class FilePath;
}
namespace ui {
class DrmDeviceHandle {
public:
DrmDeviceHandle();
~DrmDeviceHandle();
bool Initialize(const base::FilePath& path);
bool IsValid() const;
base::ScopedFD Duplicate();
private:
base::ScopedFD file_;
DISALLOW_COPY_AND_ASSIGN(DrmDeviceHandle);
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_DRM_HOST_DRM_DEVICE_HANDLE_H_
......@@ -5,7 +5,6 @@
#include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
#include <stdio.h>
#include <xf86drm.h>
#include "base/logging.h"
#include "base/thread_task_runner_handle.h"
......@@ -19,13 +18,14 @@
#include "ui/ozone/common/display_util.h"
#include "ui/ozone/common/gpu/ozone_gpu_messages.h"
#include "ui/ozone/platform/drm/host/display_manager.h"
#include "ui/ozone/platform/drm/host/drm_device_handle.h"
#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
namespace ui {
namespace {
typedef base::Callback<void(const base::FilePath&, base::File)>
typedef base::Callback<void(const base::FilePath&, scoped_ptr<DrmDeviceHandle>)>
OnOpenDeviceReplyCallback;
const char* kDisplayActionString[] = {
......@@ -34,52 +34,22 @@ const char* kDisplayActionString[] = {
"CHANGE",
};
bool Authenticate(int fd) {
drm_magic_t magic = 0;
// We need to make sure the DRM device has enough privilege. Use the DRM
// authentication logic to figure out if the device has enough permissions.
return !drmGetMagic(fd, &magic) && !drmAuthMagic(fd, magic);
}
base::File OpenDrmDevice(const base::FilePath& path) {
base::File file;
bool print_warning = true;
while (true) {
file = base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_WRITE);
base::File::Info info;
file.GetInfo(&info);
CHECK(!info.is_directory);
CHECK(path.DirName() == base::FilePath("/dev/dri"));
if (!file.IsValid()) {
LOG(ERROR) << "Failed to open " << path.value() << ": "
<< base::File::ErrorToString(file.error_details());
return file.Pass();
}
if (Authenticate(file.GetPlatformFile()))
break;
LOG_IF(WARNING, print_warning) << "Failed to authenticate " << path.value();
print_warning = false;
usleep(100000);
}
VLOG(1) << "Succeeded authenticating " << path.value();
return file.Pass();
}
void OpenDeviceOnWorkerThread(
const base::FilePath& path,
const scoped_refptr<base::TaskRunner>& reply_runner,
const OnOpenDeviceReplyCallback& callback) {
base::File file = OpenDrmDevice(path);
reply_runner->PostTask(FROM_HERE,
base::Bind(callback, path, base::Passed(file.Pass())));
scoped_ptr<DrmDeviceHandle> handle(new DrmDeviceHandle());
handle->Initialize(path);
reply_runner->PostTask(
FROM_HERE, base::Bind(callback, path, base::Passed(handle.Pass())));
}
void CloseDeviceOnWorkerThread(
scoped_ptr<DrmDeviceHandle> handle,
const scoped_refptr<base::TaskRunner>& reply_runner,
const base::Closure& callback) {
handle.reset();
reply_runner->PostTask(FROM_HERE, callback);
}
class DrmDisplaySnapshotProxy : public DisplaySnapshotProxy {
......@@ -115,15 +85,36 @@ DrmNativeDisplayDelegate::DrmNativeDisplayDelegate(
task_pending_(false),
weak_ptr_factory_(this) {
proxy_->RegisterHandler(this);
drm_devices_.insert(primary_graphics_card_path);
}
DrmNativeDisplayDelegate::~DrmNativeDisplayDelegate() {
device_manager_->RemoveObserver(this);
proxy_->UnregisterHandler(this);
for (auto it = drm_devices_.begin(); it != drm_devices_.end(); ++it) {
base::WorkerPool::PostTask(FROM_HERE,
base::Bind(&CloseDeviceOnWorkerThread,
base::Passed(drm_devices_.take(it)),
base::ThreadTaskRunnerHandle::Get(),
base::Bind(&base::DoNothing)),
false /* task_is_slow */);
}
}
void DrmNativeDisplayDelegate::Initialize() {
{
// First device needs to be treated specially. We need to open this
// synchronously since the GPU process will need it to initialize the
// graphics state.
base::ThreadRestrictions::ScopedAllowIO allow_io;
scoped_ptr<DrmDeviceHandle> handle(new DrmDeviceHandle());
if (!handle->Initialize(primary_graphics_card_path_)) {
LOG(FATAL) << "Failed to open primary graphics card";
return;
}
drm_devices_.add(primary_graphics_card_path_, handle.Pass());
}
device_manager_->AddObserver(this);
device_manager_->ScanDevices(this);
......@@ -277,7 +268,6 @@ void DrmNativeDisplayDelegate::ProcessEvent() {
switch (event.action_type) {
case DeviceEvent::ADD:
if (drm_devices_.find(event.path) == drm_devices_.end()) {
drm_devices_.insert(event.path);
task_pending_ = base::WorkerPool::PostTask(
FROM_HERE,
base::Bind(
......@@ -299,20 +289,28 @@ void DrmNativeDisplayDelegate::ProcessEvent() {
<< "Removing primary graphics card";
auto it = drm_devices_.find(event.path);
if (it != drm_devices_.end()) {
drm_devices_.erase(it);
task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask(
task_pending_ = base::WorkerPool::PostTask(
FROM_HERE,
base::Bind(&DrmNativeDisplayDelegate::OnRemoveGraphicsDevice,
weak_ptr_factory_.GetWeakPtr(), event.path));
base::Bind(
&CloseDeviceOnWorkerThread,
base::Passed(drm_devices_.take_and_erase(it)),
base::ThreadTaskRunnerHandle::Get(),
base::Bind(&DrmNativeDisplayDelegate::OnRemoveGraphicsDevice,
weak_ptr_factory_.GetWeakPtr(), event.path)),
false /* task_is_slow */);
return;
}
break;
}
}
}
void DrmNativeDisplayDelegate::OnAddGraphicsDevice(const base::FilePath& path,
base::File file) {
if (file.IsValid()) {
void DrmNativeDisplayDelegate::OnAddGraphicsDevice(
const base::FilePath& path,
scoped_ptr<DrmDeviceHandle> handle) {
if (handle->IsValid()) {
base::ScopedFD file = handle->Duplicate();
drm_devices_.add(path, handle.Pass());
proxy_->Send(new OzoneGpuMsg_AddGraphicsDevice(
path, base::FileDescriptor(file.Pass())));
FOR_EACH_OBSERVER(NativeDisplayObserver, observers_,
......@@ -343,20 +341,18 @@ void DrmNativeDisplayDelegate::OnChannelEstablished(
int host_id,
scoped_refptr<base::SingleThreadTaskRunner> send_runner,
const base::Callback<void(IPC::Message*)>& send_callback) {
drm_devices_.clear();
drm_devices_.insert(primary_graphics_card_path_);
{
// First device needs to be treated specially. We need to open this
// synchronously since the GPU process will need it to initialize the
// graphics state.
base::ThreadRestrictions::ScopedAllowIO allow_io;
base::File file = OpenDrmDevice(primary_graphics_card_path_);
if (!file.IsValid()) {
LOG(FATAL) << "Failed to open primary graphics card";
return;
auto it = drm_devices_.find(primary_graphics_card_path_);
DCHECK(it != drm_devices_.end());
// Send the primary device first since this is used to initialize graphics
// state.
proxy_->Send(new OzoneGpuMsg_AddGraphicsDevice(
it->first, base::FileDescriptor(it->second->Duplicate())));
for (auto pair : drm_devices_) {
if (pair.second->IsValid() && pair.first != primary_graphics_card_path_) {
proxy_->Send(new OzoneGpuMsg_AddGraphicsDevice(
pair.first, base::FileDescriptor(pair.second->Duplicate())));
}
proxy_->Send(new OzoneGpuMsg_AddGraphicsDevice(
primary_graphics_card_path_, base::FileDescriptor(file.Pass())));
}
device_manager_->ScanDevices(this);
......
......@@ -7,12 +7,11 @@
#include <map>
#include <queue>
#include <set>
#include "base/containers/scoped_ptr_hash_map.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.h"
#include "base/observer_list.h"
#include "ui/display/types/native_display_delegate.h"
......@@ -24,6 +23,7 @@ namespace ui {
class DeviceManager;
class DisplayManager;
class DrmDeviceHandle;
class DrmGpuPlatformSupportHost;
struct DisplaySnapshot_Params;
......@@ -97,7 +97,8 @@ class DrmNativeDisplayDelegate : public NativeDisplayDelegate,
// Called as a result of finishing to process the display hotplug event. These
// are responsible for dequing the event and scheduling the next event.
void OnAddGraphicsDevice(const base::FilePath& path, base::File file);
void OnAddGraphicsDevice(const base::FilePath& path,
scoped_ptr<DrmDeviceHandle> handle);
void OnUpdateGraphicsDevice();
void OnRemoveGraphicsDevice(const base::FilePath& path);
......@@ -141,7 +142,8 @@ class DrmNativeDisplayDelegate : public NativeDisplayDelegate,
bool task_pending_;
// Keeps track of all the active DRM devices.
std::set<base::FilePath> drm_devices_;
base::ScopedPtrHashMap<base::FilePath, scoped_ptr<DrmDeviceHandle>>
drm_devices_;
base::WeakPtrFactory<DrmNativeDisplayDelegate> weak_ptr_factory_;
......
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