uterm: add PCI primary GPU detection

We used to simply probe every GPU that is reported by the kernel. However,
if a system has multiple GPUs that share display controllers, we cannot
use both simultaneously. Unfortunately, the kernel currently does not
notify us about this. Hence, we use some heuristics to determine which GPU
is the boot-gpu/primary-gpu.

This only adds the detection logic, it does not modify any code to use
this detection at all.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2012-10-27 13:25:44 +02:00
parent 2c5e9bff2b
commit 89e84aa78c
5 changed files with 322 additions and 0 deletions

View File

@ -178,6 +178,7 @@ libuterm_la_SOURCES = \
src/uterm.h \
src/uterm_input.h \
src/uterm_video.h \
src/uterm_pci.h \
src/uterm_video.c \
src/uterm_monitor.c \
src/uterm_vt.c \
@ -205,6 +206,12 @@ libuterm_la_CPPFLAGS += $(UDEV_CFLAGS)
libuterm_la_LIBADD += $(UDEV_LIBS)
endif
if BUILD_ENABLE_PCIACCESS
libuterm_la_CPPFLAGS += $(PCIACCESS_CFLAGS)
libuterm_la_LIBADD += $(PCIACCESS_LIBS)
libuterm_la_SOURCES += src/uterm_pci.c
endif
if BUILD_ENABLE_VIDEO_FBDEV
libuterm_la_SOURCES += src/uterm_video_fbdev.c
endif

View File

@ -103,6 +103,11 @@ PKG_CHECK_MODULES([FUSE], [fuse],
AC_SUBST(FUSE_CFLAGS)
AC_SUBST(FUSE_LIBS)
PKG_CHECK_MODULES([PCIACCESS], [pciaccess],
[have_pciaccess=yes], [have_pciaccess=no])
AC_SUBST(PCIACCESS_CFLAGS)
AC_SUBST(PCIACCESS_LIBS)
#
# Parse arguments
# This parses all arguments that are given via "--enable-XY" or "--with-XY" and
@ -200,6 +205,16 @@ if test "x$enable_hotplug" = "x" ; then
fi
AC_MSG_RESULT([$enable_hotplug])
# pciaccess
AC_MSG_CHECKING([whether user wants pciaccess device detection])
AC_ARG_ENABLE([pciaccess],
[AS_HELP_STRING([--enable-pciaccess],
[enable device detection via pciaccess])])
if test "x$enable_pciaccess" = "x" ; then
enable_pciaccess="yes (default)"
fi
AC_MSG_RESULT([$enable_pciaccess])
# eloop-dbus
AC_MSG_CHECKING([whether user wants eloop dbus support])
AC_ARG_ENABLE([eloop-dbus],
@ -455,6 +470,16 @@ if test ! "x$enable_hotplug" = "xno" ; then
fi
fi
# pciaccess
pciaccess_avail=no
if test ! "x$enable_pciaccess" = "xno" ; then
if test "x$have_pciaccess" = "xyes" ; then
pciaccess_avail=yes
elif test "x$enable_pciaccess" = "xyes" ; then
AC_ERROR([pciaccess libraries not found for pciaccess device detection])
fi
fi
# renderer bblit
renderer_bblit_avail=no
if test ! "x$enable_renderer_bblit" = "xno" ; then
@ -714,6 +739,14 @@ if test "x$renderer_bblit_avail" = "xyes" ; then
fi
fi
# pciaccess
pciaccess_enabled=no
if test "x$pciaccess_avail" = "xyes" ; then
if test "x${enable_pciaccess% *}" = "xyes" ; then
pciaccess_enabled=yes
fi
fi
# hotplug
hotplug_enabled=no
if test "x$hotplug_avail" = "xyes" ; then
@ -885,6 +918,15 @@ fi
AM_CONDITIONAL([BUILD_ENABLE_HOTPLUG],
[test "x$hotplug_enabled" = "xyes"])
# pciaccess
if test "x$pciaccess_enabled" = "xyes" ; then
AC_DEFINE([BUILD_ENABLE_PCIACCESS], [1],
[Use pciaccess for device detection])
fi
AM_CONDITIONAL([BUILD_ENABLE_PCIACCESS],
[test "x$pciaccess_enabled" = "xyes"])
# renderer bblit
if test "x$renderer_bblit_enabled" = "xyes" ; then
AC_DEFINE([BUILD_ENABLE_RENDERER_BBLIT], [1],
@ -1060,6 +1102,7 @@ AC_MSG_NOTICE([Build configuration:
optimizations: $optimizations_enabled ($optimizations_avail)
multi-seat: $multi_seat_enabled ($multi_seat_avail)
hotplug: $hotplug_enabled ($hotplug_avail)
pciaccess: $pciaccess_enabled ($pciaccess_avail)
eloop-dbus: $eloop_dbus_enabled ($eloop_dbus_avail)
Video Backends:

View File

@ -43,6 +43,7 @@
#include "log.h"
#include "shl_dlist.h"
#include "uterm.h"
#include "uterm_pci.h"
#ifdef BUILD_ENABLE_MULTI_SEAT
#include <systemd/sd-login.h>
@ -77,6 +78,8 @@ struct uterm_monitor {
struct ev_fd *sd_mon_fd;
#endif
char *pci_primary_id;
struct udev *udev;
struct udev_monitor *umon;
struct ev_fd *umon_fd;
@ -669,6 +672,10 @@ int uterm_monitor_new(struct uterm_monitor **out,
mon->data = data;
shl_dlist_init(&mon->seats);
ret = uterm_pci_get_primary_id(&mon->pci_primary_id);
if (ret)
log_warning("cannot get PCI primary ID");
ret = monitor_sd_init(mon);
if (ret)
goto err_free;
@ -760,6 +767,7 @@ err_udev:
err_sd:
monitor_sd_deinit(mon);
err_free:
free(mon->pci_primary_id);
free(mon);
return ret;
}
@ -791,6 +799,7 @@ void uterm_monitor_unref(struct uterm_monitor *mon)
udev_unref(mon->udev);
monitor_sd_deinit(mon);
ev_eloop_unref(mon->eloop);
free(mon->pci_primary_id);
free(mon);
}

213
src/uterm_pci.c Normal file
View File

@ -0,0 +1,213 @@
/*
* uterm - Linux User-Space Terminal
*
* Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.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 the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* PCI Helpers
* This uses the pciaccess library to retrieve information from the PCI bus.
*/
#include <errno.h>
#include <inttypes.h>
#include <pciaccess.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "log.h"
#include "uterm.h"
#include "uterm_pci.h"
#define LOG_SUBSYSTEM "pci"
/* pci classes */
#define UTERM_PCI_CLASS_PREHISTORIC 0x00
#define UTERM_PCI_CLASS_DISPLAY 0x03
#define UTERM_PCI_CLASS_MULTIMEDIA 0x04
#define UTERM_PCI_CLASS_PROCESSOR 0x0b
/* pci sub-classes */
#define UTERM_PCI_SUBCLASS_DISPLAY_VGA 0x00
#define UTERM_PCI_SUBCLASS_MULTIMEDIA_VIDEO 0x00
#define UTERM_PCI_SUBCLASS_PROCESSOR_COPROC 0x40
/* pci registers */
#define UTERM_PCI_CMD_MEM_ENABLE 0x02
static bool uterm_pci_is_gpu(struct pci_device *dev)
{
uint32_t class = dev->device_class;
uint32_t match;
match = UTERM_PCI_CLASS_PREHISTORIC << 16;
if ((class & 0x00ff0000) == match)
return true;
match = UTERM_PCI_CLASS_DISPLAY << 16;
if ((class & 0x00ff0000) == match)
return true;
match = UTERM_PCI_CLASS_MULTIMEDIA << 16;
match |= UTERM_PCI_SUBCLASS_MULTIMEDIA_VIDEO << 8;
if ((class & 0x00ffff00) == match)
return true;
match = UTERM_PCI_CLASS_PROCESSOR << 16;
match |= UTERM_PCI_SUBCLASS_PROCESSOR_COPROC << 8;
if ((class & 0x00ffff00) == match)
return true;
return false;
}
static bool uterm_pci_is_vga(struct pci_device *dev)
{
uint32_t class = dev->device_class;
uint32_t match;
match = UTERM_PCI_CLASS_DISPLAY << 16;
match |= UTERM_PCI_SUBCLASS_DISPLAY_VGA << 8;
return (class & 0x00ffff00) == match;
}
static const struct pci_slot_match uterm_pci_match = {
.domain = PCI_MATCH_ANY,
.bus = PCI_MATCH_ANY,
.dev = PCI_MATCH_ANY,
.func = PCI_MATCH_ANY,
.match_data = 0,
};
#define UTERM_PCI_FORMAT "pci:%04x:%02x:%02x.%d"
int uterm_pci_get_primary_id(char **out)
{
int ret;
struct pci_device_iterator *iter;
struct pci_device *dev;
char *buf;
uint16_t cmd;
unsigned int num;
if (!out)
return -EINVAL;
ret = pci_system_init();
if (ret) {
log_error("cannot initialize pciaccess library (%d/%d): %m",
ret, errno);
return -EFAULT;
}
iter = pci_slot_match_iterator_create(&uterm_pci_match);
if (!iter) {
log_error("cannot create pci-slot iterator (%d): %m",
errno);
ret = -EFAULT;
goto out_cleanup;
}
buf = NULL;
num = 0;
while ((dev = pci_device_next(iter))) {
if (!uterm_pci_is_gpu(dev))
continue;
++num;
if (!pci_device_is_boot_vga(dev))
continue;
log_debug("primary PCI GPU: " UTERM_PCI_FORMAT,
dev->domain, dev->bus, dev->dev, dev->func);
if (buf) {
log_warning("multiple primary PCI GPUs found");
continue;
}
ret = asprintf(&buf, UTERM_PCI_FORMAT, dev->domain, dev->bus,
dev->dev, dev->func);
if (ret < 0) {
log_error("cannot allocate memory for PCI name");
goto out_iter;
}
}
free(iter);
if (buf) {
ret = 0;
*out = buf;
goto out_cleanup;
}
/* If no GPU is marked as boot_vga, we try finding a VGA card */
iter = pci_slot_match_iterator_create(&uterm_pci_match);
if (!iter) {
log_error("cannot create pci-slot iterator (%d): %m",
errno);
ret = -EFAULT;
goto out_cleanup;
}
while ((dev = pci_device_next(iter))) {
if (!uterm_pci_is_gpu(dev))
continue;
ret = pci_device_cfg_read_u16(dev, &cmd, 4);
if (ret)
continue;
if (!(cmd & UTERM_PCI_CMD_MEM_ENABLE))
continue;
if (num != 1 && !uterm_pci_is_vga(dev))
continue;
log_debug("primary PCI VGA GPU: " UTERM_PCI_FORMAT,
dev->domain, dev->bus, dev->dev, dev->func);
if (buf) {
log_warning("multiple primary PCI VGA GPUs found");
continue;
}
ret = asprintf(&buf, UTERM_PCI_FORMAT, dev->domain, dev->bus,
dev->dev, dev->func);
if (ret < 0) {
log_error("cannot allocate memory for PCI name");
goto out_iter;
}
}
if (buf) {
ret = 0;
*out = buf;
} else {
log_warning("no primary PCI GPU found");
ret = -ENOENT;
}
out_iter:
free(iter);
out_cleanup:
pci_system_cleanup();
return ret;
}

50
src/uterm_pci.h Normal file
View File

@ -0,0 +1,50 @@
/*
* uterm - Linux User-Space Terminal
*
* Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.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 the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* PCI Helpers
* This uses the pciaccess library to retrieve information from the PCI bus.
*/
#ifndef UTERM_PCI_H
#define UTERM_PCI_H
#include <stdlib.h>
#include "uterm.h"
#ifdef BUILD_ENABLE_PCIACCESS
int uterm_pci_get_primary_id(char **out);
#else
static inline int uterm_pci_get_primary_id(char **out)
{
return -EOPNOTSUPP;
}
#endif
#endif /* UTERM_PCI_H */