intel_fb: handle too large framebuffer allocation

In case the allocation fails for the largest possible connector mode,
try smaller modes. Additional add option to specify maximal resolution.

Issue #4659
This commit is contained in:
Alexander Boettcher 2022-11-02 16:02:43 +01:00 committed by Christian Helmuth
parent 72217a6771
commit eb318d5ceb
10 changed files with 145 additions and 19 deletions

View File

@ -16,6 +16,12 @@ if {[get_cmd_switch --autopilot] && [have_board linux]} {
set use_gpu 1
set use_top 0
set use_fb_controller 0
if {$use_fb_controller} {
set apply_on_hotplug "no"
} else {
set apply_on_hotplug "yes"
}
set build_components {
core init timer
@ -193,7 +199,7 @@ append config {
<ram/>
<import>
<inline name="fb_drv.config">
<config ld_verbose="yes">
<config ld_verbose="yes" apply_on_hotplug="} $apply_on_hotplug {">
<report connectors="yes"/>
</config>
</inline>
@ -253,11 +259,12 @@ append config {
<service name="Report"> <child name="report_rom"/> </service>
<any-service> <parent/> <any-child /> </any-service>
</route>
</start>
</start>}
append_if $use_fb_controller {
<start name="intel_fb_controller" priority="-1">
<resource name="RAM" quantum="1M"/>
<config artifical_update_ms="0"> <!-- off -->
<config>
<vfs> <fs/> </vfs>
</config>
<route>
@ -265,8 +272,9 @@ append config {
<service name="ROM" label="connectors"> <child name="report_rom"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
</start>}
append config {
<start name="test-framebuffer" priority="-1">
<resource name="RAM" quantum="10M"/>
<provides> <service name="Capture"/> </provides>

View File

@ -39,6 +39,11 @@ about the change in resolution.
The brightness value is in percent and takes effect only if supported by
the hardware.
The maximal physical resolution can be enforced by:
! <config max_width="2560" max_height="1440">
! </config>
The virtual resolution can be enforced by:
! <config force_width="1024" force_height="768">

View File

@ -20,6 +20,9 @@
void *emul_alloc_shmem_file_buffer(unsigned long size)
{
if (!size)
return nullptr;
auto &buffer = Lx_kit::env().memory.alloc_buffer(size);
return reinterpret_cast<void *>(buffer.virt_addr());
}

View File

@ -246,6 +246,16 @@ int i915_gem_init(struct drm_i915_private *dev_priv)
{
int ret;
/* request & enforce max resolution if smaller than hardware limits */
struct genode_mode dummy_mode = { };
lx_emul_i915_connector_config("dummy", &dummy_mode);
if (dummy_mode.max_width && dummy_mode.max_height) {
if (dev_priv->drm.mode_config.max_width > dummy_mode.max_width)
dev_priv->drm.mode_config.max_width = dummy_mode.max_width;
if (dev_priv->drm.mode_config.max_height > dummy_mode.max_height)
dev_priv->drm.mode_config.max_height = dummy_mode.max_height;
}
/* We need to fallback to 4K pages if host doesn't support huge gtt. */
/*
if (intel_vgpu_active(dev_priv) && !intel_vgpu_has_huge_gtt(dev_priv))

View File

@ -521,14 +521,6 @@ bool file_ns_capable(const struct file * file,struct user_namespace * ns,int cap
}
#include <linux/file.h>
void fput(struct file * file)
{
lx_emul_trace_and_stop(__func__);
}
#include <linux/fb.h>
void framebuffer_release(struct fb_info * info)

View File

@ -108,6 +108,9 @@ struct file *shmem_file_setup(char const *name, loff_t size,
struct shmem_file_buffer *private_data;
loff_t const nrpages = (size / PAGE_SIZE) + ((size % (PAGE_SIZE)) ? 1 : 0);
if (!size)
return (struct file*)ERR_PTR(-EINVAL);
f = kzalloc(sizeof (struct file), 0);
if (!f) {
return (struct file*)ERR_PTR(-ENOMEM);
@ -165,6 +168,35 @@ err_inode:
}
static void _free_file(struct file *file)
{
struct inode *inode;
struct address_space *mapping;
struct shmem_file_buffer *private_data;
mapping = file->f_mapping;
inode = file->f_inode;
private_data = mapping->private_data;
lx_emul_forget_pages(private_data->addr, mapping->nrpages << 12);
emul_free_shmem_file_buffer(private_data->addr);
kfree(private_data);
kfree(mapping);
kfree(inode);
kfree(file->f_path.dentry);
kfree(file);
}
void fput(struct file *file)
{
if (atomic_long_sub_and_test(1, &file->f_count)) {
_free_file(file);
}
}
struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
pgoff_t index, gfp_t gfp)
{

View File

@ -29,6 +29,7 @@ struct dma_fence_work_ops;
void lx_emul_time_udelay(unsigned long usec);
void *emul_alloc_shmem_file_buffer(unsigned long);
void emul_free_shmem_file_buffer(void *);
void * intel_io_mem_map(unsigned long offset, unsigned long size);

View File

@ -17,6 +17,8 @@
struct genode_mode {
unsigned width;
unsigned height;
unsigned max_width;
unsigned max_height;
unsigned hz;
unsigned brightness;
unsigned enabled;

View File

@ -36,7 +36,7 @@ static struct drm_fb_helper * i915_fb(void) { return &i915->fbdev->helper; }
/*
* Heuristic to calculate max resolution across all connectors
*/
static void preferred_mode(struct drm_display_mode *prefer)
static void preferred_mode(struct drm_display_mode *prefer, uint64_t smaller_as)
{
struct drm_connector *connector = NULL;
struct drm_display_mode *mode = NULL;
@ -68,6 +68,15 @@ static void preferred_mode(struct drm_display_mode *prefer)
}
}
/* maximal resolution enforcement */
if (conf_mode.max_width && conf_mode.max_height) {
if (conf_mode.max_width * conf_mode.height < smaller_as)
smaller_as = conf_mode.max_width * conf_mode.max_height + 1;
if (conf_mode.max_width * conf_mode.height < prefer->hdisplay * prefer->vdisplay)
continue;
}
if (!conf_mode.width || !conf_mode.height)
continue;
@ -78,7 +87,13 @@ static void preferred_mode(struct drm_display_mode *prefer)
}
drm_connector_list_iter_end(&conn_iter);
/* if nothing was configured by Genode's config, apply heuristic */
/* too large check */
if (smaller_as <= (uint64_t)prefer->hdisplay * prefer->vdisplay) {
prefer->hdisplay = 0;
prefer->vdisplay = 0;
}
/* if too large or nothing configured by Genode's config */
if (!prefer->hdisplay || !prefer->vdisplay) {
drm_connector_list_iter_begin(i915_fb()->dev, &conn_iter);
drm_client_for_each_connector_iter(connector, &conn_iter) {
@ -86,6 +101,9 @@ static void preferred_mode(struct drm_display_mode *prefer)
if (!mode)
continue;
if (smaller_as <= (uint64_t)mode->hdisplay * mode->vdisplay)
continue;
if (mode->hdisplay * mode->vdisplay > prefer->hdisplay * prefer->vdisplay) {
prefer->hdisplay = mode->hdisplay;
prefer->vdisplay = mode->vdisplay;
@ -134,6 +152,9 @@ static unsigned get_brightness(struct drm_connector * const connector,
static bool reconfigure(void * data)
{
static uint64_t width_smaller_as = 100000;
static uint64_t height_smaller_as = 100000;
struct drm_display_mode *mode = NULL;
struct drm_display_mode mode_preferred = {};
struct drm_mode_set *mode_set = NULL;
@ -147,7 +168,7 @@ static bool reconfigure(void * data)
BUG_ON(!i915_fb()->funcs);
BUG_ON(!i915_fb()->funcs->fb_probe);
preferred_mode(&mode_preferred);
preferred_mode(&mode_preferred, width_smaller_as * height_smaller_as);
if (mode_preferred.hdisplay && mode_preferred.vdisplay) {
unsigned err = 0;
@ -161,10 +182,44 @@ static bool reconfigure(void * data)
sizes.surface_height = sizes.fb_height;
err = (*i915_fb()->funcs->fb_probe)(i915_fb(), &sizes);
/* i915_fb()->fb contains adjusted drm_frambuffer object */
/* i915_fb()->fb contains adjusted drm_framebuffer object */
if (err || !i915_fb()->fbdev)
printk("setting up framebuffer failed - error=%d\n", err);
if (err || !i915_fb()->fbdev) {
printk("setting up framebuffer of %ux%u failed - error=%d\n",
mode_preferred.hdisplay, mode_preferred.vdisplay, err);
if (err == -ENOMEM) {
/*
* roll back code for intelfb_create() in
* drivers/gpu/drm/i915/display/intel_fbdev.c:
*
* vma = intel_pin_and_fence_fb_obj(&ifbdev->fb->base, false,
* &view, false, &flags);
* if (IS_ERR(vma)) {
*
* If the partial allocation is not reverted, the next
* i915_fb()->funcs->fb_probe (which calls intelfb_create)
* will try the old resolution, which failed and fails again,
* instead of using the new smaller resolution.
*/
struct intel_fbdev *ifbdev =
container_of(i915_fb(), struct intel_fbdev, helper);
if (ifbdev && ifbdev->fb) {
drm_framebuffer_put(&ifbdev->fb->base);
ifbdev->fb = NULL;
}
width_smaller_as = mode_preferred.hdisplay;
height_smaller_as = mode_preferred.vdisplay;
retry = true;
return retry;
}
} else {
width_smaller_as = 100000;
height_smaller_as = 100000;
}
}
if (!i915_fb()->fb)
@ -203,6 +258,11 @@ static bool reconfigure(void * data)
if (!mode)
continue;
/* allocated framebuffer smaller than mode can't be used */
if (report_fb_info.var.xres * report_fb_info.var.yres <
mode->vdisplay * mode->hdisplay)
continue;
/* use mode id if configured and matches exactly */
if (conf_mode.id) {
if (conf_mode.id != mode_id)
@ -248,7 +308,13 @@ static bool reconfigure(void * data)
/* no matching mode ? */
if (!mode_match) {
/* use first mode */
/* allocated framebuffer smaller than mode can't be used */
if (report_fb_info.var.xres * report_fb_info.var.yres <
mode->vdisplay * mode->hdisplay)
continue;
/* use first smaller mode */
mode_match = mode;
if (conf_mode.enabled)

View File

@ -192,6 +192,8 @@ void Framebuffer::Driver::lookup_config(char const * const name,
unsigned force_width = config.xml().attribute_value("force_width", 0u);
unsigned force_height = config.xml().attribute_value("force_height", 0u);
unsigned max_width = config.xml().attribute_value("max_width", 0u);
unsigned max_height = config.xml().attribute_value("max_height", 0u);
/* iterate independently of force* ever to get brightness and hz */
config.xml().for_each_sub_node("connector", [&] (Xml_node &node) {
@ -220,6 +222,11 @@ void Framebuffer::Driver::lookup_config(char const * const name,
mode.height = force_height;
mode.id = 0;
}
if (max_width && max_height) {
mode.max_width = max_width;
mode.max_height = max_height;
}
}