Commit a7e2641a authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'drm-intel-next-2017-01-23' of git://anongit.freedesktop.org/git/drm-intel into drm-next

Final block of feature work for 4.11:

- gen8 pd cleanup from Matthew Auld
- more cleanups for view/vma (Chris)
- dmc support on glk (Anusha Srivatsa)
- use core crc api (Tomue)
- track wedged requests using fence.error (Chris)
- lots of psr fixes (Nagaraju, Vathsala)
- dp mst support, acked for merging through drm-intel by Takashi
  (Libin)
- huc loading support, including uapi for libva to use it (Anusha
  Srivatsa)

* tag 'drm-intel-next-2017-01-23' of git://anongit.freedesktop.org/git/drm-intel: (111 commits)
  drm/i915: Update DRIVER_DATE to 20170123
  drm/i915: reinstate call to trace_i915_vma_bind
  drm/i915: Assert that created vma has a whole number of pages
  drm/i915: Assert the drm_mm_node is allocated when on the VM lists
  drm/i915: Treat an error from i915_vma_instance() as unlikely
  drm/i915: Reject vma creation larger than address space
  drm/i915: Use common LRU inactive vma bumping for unpin_from_display
  drm/i915: Do an unlocked wait before set-cache-level ioctl
  drm/i915/huc: Assert that HuC vma is placed in GuC accessible range
  drm/i915/huc: Avoid attempting to authenticate non-existent fw
  drm/i915: Set adjustment to zero on Up/Down interrupts if freq is already max/min
  drm/i915: Remove the double handling of 'flags from intel_mode_from_pipe_config()
  drm/i915: Remove crtc->config usage from intel_modeset_readout_hw_state()
  drm/i915: Release temporary load-detect state upon switching
  drm/i915: Remove i915_gem_object_to_ggtt()
  drm/i915: Remove i915_vma_create from VMA API
  drm/i915: Add a check that the VMA instance we lookup matches the request
  drm/i915: Rename some warts in the VMA API
  drm/i915: Track pinned vma in intel_plane_state
  drm/i915/get_params: Add HuC status to getparams
  ...
parents c4d79c22 add6329c
......@@ -19,6 +19,23 @@ PCM
===
To be added
Pin Initialization
==================
Each pin may have several device entries (virtual pins). On Intel platform,
the device entries number is dynamically changed. If DP MST hub is connected,
it is in DP MST mode, and the device entries number is 3. Otherwise, the
device entries number is 1.
To simplify the implementation, all the device entries will be initialized
when bootup no matter whether it is in DP MST mode or not.
Connection list
===============
DP MST reuses connection list code. The code can be reused because
device entries on the same pin have the same connection list.
This means DP MST gets the device entry connection list without the
device entry setting.
Jack
====
......
......@@ -56,7 +56,9 @@ i915-y += i915_cmd_parser.o \
# general-purpose microcontroller (GuC) support
i915-y += intel_uc.o \
intel_guc_log.o \
intel_guc_loader.o \
intel_huc.o \
i915_guc_submission.o
# autogenerated null render state
......
......@@ -41,47 +41,34 @@ static int alloc_gm(struct intel_vgpu *vgpu, bool high_gm)
{
struct intel_gvt *gvt = vgpu->gvt;
struct drm_i915_private *dev_priv = gvt->dev_priv;
u32 alloc_flag, search_flag;
unsigned int flags;
u64 start, end, size;
struct drm_mm_node *node;
int retried = 0;
int ret;
if (high_gm) {
search_flag = DRM_MM_SEARCH_BELOW;
alloc_flag = DRM_MM_CREATE_TOP;
node = &vgpu->gm.high_gm_node;
size = vgpu_hidden_sz(vgpu);
start = gvt_hidden_gmadr_base(gvt);
end = gvt_hidden_gmadr_end(gvt);
flags = PIN_HIGH;
} else {
search_flag = DRM_MM_SEARCH_DEFAULT;
alloc_flag = DRM_MM_CREATE_DEFAULT;
node = &vgpu->gm.low_gm_node;
size = vgpu_aperture_sz(vgpu);
start = gvt_aperture_gmadr_base(gvt);
end = gvt_aperture_gmadr_end(gvt);
flags = PIN_MAPPABLE;
}
mutex_lock(&dev_priv->drm.struct_mutex);
search_again:
ret = drm_mm_insert_node_in_range_generic(&dev_priv->ggtt.base.mm,
node, size, 4096,
I915_COLOR_UNEVICTABLE,
start, end, search_flag,
alloc_flag);
if (ret) {
ret = i915_gem_evict_something(&dev_priv->ggtt.base,
size, 4096,
I915_COLOR_UNEVICTABLE,
start, end, 0);
if (ret == 0 && ++retried < 3)
goto search_again;
gvt_err("fail to alloc %s gm space from host, retried %d\n",
high_gm ? "high" : "low", retried);
}
ret = i915_gem_gtt_insert(&dev_priv->ggtt.base, node,
size, 4096, I915_COLOR_UNEVICTABLE,
start, end, flags);
mutex_unlock(&dev_priv->drm.struct_mutex);
if (ret)
gvt_err("fail to alloc %s gm space from host\n",
high_gm ? "high" : "low");
return ret;
}
......
......@@ -159,8 +159,35 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
seq_printf(m, " (%sgtt offset: %08llx, size: %08llx",
i915_vma_is_ggtt(vma) ? "g" : "pp",
vma->node.start, vma->node.size);
if (i915_vma_is_ggtt(vma))
seq_printf(m, ", type: %u", vma->ggtt_view.type);
if (i915_vma_is_ggtt(vma)) {
switch (vma->ggtt_view.type) {
case I915_GGTT_VIEW_NORMAL:
seq_puts(m, ", normal");
break;
case I915_GGTT_VIEW_PARTIAL:
seq_printf(m, ", partial [%08llx+%x]",
vma->ggtt_view.partial.offset << PAGE_SHIFT,
vma->ggtt_view.partial.size << PAGE_SHIFT);
break;
case I915_GGTT_VIEW_ROTATED:
seq_printf(m, ", rotated [(%ux%u, stride=%u, offset=%u), (%ux%u, stride=%u, offset=%u)]",
vma->ggtt_view.rotated.plane[0].width,
vma->ggtt_view.rotated.plane[0].height,
vma->ggtt_view.rotated.plane[0].stride,
vma->ggtt_view.rotated.plane[0].offset,
vma->ggtt_view.rotated.plane[1].width,
vma->ggtt_view.rotated.plane[1].height,
vma->ggtt_view.rotated.plane[1].stride,
vma->ggtt_view.rotated.plane[1].offset);
break;
default:
MISSING_CASE(vma->ggtt_view.type);
break;
}
}
if (vma->fence)
seq_printf(m, " , fence: %d%s",
vma->fence->id,
......@@ -2325,10 +2352,40 @@ static int i915_llc(struct seq_file *m, void *data)
return 0;
}
static int i915_huc_load_status_info(struct seq_file *m, void *data)
{
struct drm_i915_private *dev_priv = node_to_i915(m->private);
struct intel_uc_fw *huc_fw = &dev_priv->huc.fw;
if (!HAS_HUC_UCODE(dev_priv))
return 0;
seq_puts(m, "HuC firmware status:\n");
seq_printf(m, "\tpath: %s\n", huc_fw->path);
seq_printf(m, "\tfetch: %s\n",
intel_uc_fw_status_repr(huc_fw->fetch_status));
seq_printf(m, "\tload: %s\n",
intel_uc_fw_status_repr(huc_fw->load_status));
seq_printf(m, "\tversion wanted: %d.%d\n",
huc_fw->major_ver_wanted, huc_fw->minor_ver_wanted);
seq_printf(m, "\tversion found: %d.%d\n",
huc_fw->major_ver_found, huc_fw->minor_ver_found);
seq_printf(m, "\theader: offset is %d; size = %d\n",
huc_fw->header_offset, huc_fw->header_size);
seq_printf(m, "\tuCode: offset is %d; size = %d\n",
huc_fw->ucode_offset, huc_fw->ucode_size);
seq_printf(m, "\tRSA: offset is %d; size = %d\n",
huc_fw->rsa_offset, huc_fw->rsa_size);
seq_printf(m, "\nHuC status 0x%08x:\n", I915_READ(HUC_STATUS2));
return 0;
}
static int i915_guc_load_status_info(struct seq_file *m, void *data)
{
struct drm_i915_private *dev_priv = node_to_i915(m->private);
struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw;
struct intel_uc_fw *guc_fw = &dev_priv->guc.fw;
u32 tmp, i;
if (!HAS_GUC_UCODE(dev_priv))
......@@ -2336,15 +2393,15 @@ static int i915_guc_load_status_info(struct seq_file *m, void *data)
seq_printf(m, "GuC firmware status:\n");
seq_printf(m, "\tpath: %s\n",
guc_fw->guc_fw_path);
guc_fw->path);
seq_printf(m, "\tfetch: %s\n",
intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status));
intel_uc_fw_status_repr(guc_fw->fetch_status));
seq_printf(m, "\tload: %s\n",
intel_guc_fw_status_repr(guc_fw->guc_fw_load_status));
intel_uc_fw_status_repr(guc_fw->load_status));
seq_printf(m, "\tversion wanted: %d.%d\n",
guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted);
guc_fw->major_ver_wanted, guc_fw->minor_ver_wanted);
seq_printf(m, "\tversion found: %d.%d\n",
guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found);
guc_fw->major_ver_found, guc_fw->minor_ver_found);
seq_printf(m, "\theader: offset is %d; size = %d\n",
guc_fw->header_offset, guc_fw->header_size);
seq_printf(m, "\tuCode: offset is %d; size = %d\n",
......@@ -2532,6 +2589,29 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_guc_log_control_fops,
i915_guc_log_control_get, i915_guc_log_control_set,
"%lld\n");
static const char *psr2_live_status(u32 val)
{
static const char * const live_status[] = {
"IDLE",
"CAPTURE",
"CAPTURE_FS",
"SLEEP",
"BUFON_FW",
"ML_UP",
"SU_STANDBY",
"FAST_SLEEP",
"DEEP_SLEEP",
"BUF_ON",
"TG_ON"
};
val = (val & EDP_PSR2_STATUS_STATE_MASK) >> EDP_PSR2_STATUS_STATE_SHIFT;
if (val < ARRAY_SIZE(live_status))
return live_status[val];
return "unknown";
}
static int i915_edp_psr_status(struct seq_file *m, void *data)
{
struct drm_i915_private *dev_priv = node_to_i915(m->private);
......@@ -2606,6 +2686,12 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
seq_printf(m, "Performance_Counter: %u\n", psrperf);
}
if (dev_priv->psr.psr2_support) {
u32 psr2 = I915_READ(EDP_PSR2_STATUS_CTL);
seq_printf(m, "EDP_PSR2_STATUS_CTL: %x [%s]\n",
psr2, psr2_live_status(psr2));
}
mutex_unlock(&dev_priv->psr.lock);
intel_runtime_pm_put(dev_priv);
......@@ -4553,6 +4639,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"i915_guc_info", i915_guc_info, 0},
{"i915_guc_load_status", i915_guc_load_status_info, 0},
{"i915_guc_log_dump", i915_guc_log_dump, 0},
{"i915_huc_load_status", i915_huc_load_status_info, 0},
{"i915_frequency_info", i915_frequency_info, 0},
{"i915_hangcheck_info", i915_hangcheck_info, 0},
{"i915_drpc_info", i915_drpc_info, 0},
......
......@@ -49,6 +49,7 @@
#include "i915_trace.h"
#include "i915_vgpu.h"
#include "intel_drv.h"
#include "intel_uc.h"
static struct drm_driver driver;
......@@ -315,6 +316,12 @@ static int i915_getparam(struct drm_device *dev, void *data,
case I915_PARAM_MIN_EU_IN_POOL:
value = INTEL_INFO(dev_priv)->sseu.min_eu_in_pool;
break;
case I915_PARAM_HUC_STATUS:
/* The register is already force-woken. We dont need
* any rpm here
*/
value = I915_READ(HUC_STATUS2) & HUC_FW_VERIFIED;
break;
case I915_PARAM_MMAP_GTT_VERSION:
/* Though we've started our numbering from 1, and so class all
* earlier versions as 0, in effect their value is undefined as
......@@ -599,6 +606,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret)
goto cleanup_irq;
intel_huc_init(dev_priv);
intel_guc_init(dev_priv);
ret = i915_gem_init(dev_priv);
......@@ -627,6 +635,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
i915_gem_fini(dev_priv);
cleanup_irq:
intel_guc_fini(dev_priv);
intel_huc_fini(dev_priv);
drm_irq_uninstall(dev);
intel_teardown_gmbus(dev_priv);
cleanup_csr:
......@@ -1114,7 +1123,7 @@ static void i915_driver_register(struct drm_i915_private *dev_priv)
/* Reveal our presence to userspace */
if (drm_dev_register(dev, 0) == 0) {
i915_debugfs_register(dev_priv);
i915_guc_register(dev_priv);
i915_guc_log_register(dev_priv);
i915_setup_sysfs(dev_priv);
/* Depends on sysfs having been initialized */
......@@ -1158,7 +1167,7 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv)
i915_perf_unregister(dev_priv);
i915_teardown_sysfs(dev_priv);
i915_guc_unregister(dev_priv);
i915_guc_log_unregister(dev_priv);
i915_debugfs_unregister(dev_priv);
drm_dev_unregister(&dev_priv->drm);
......@@ -1314,6 +1323,7 @@ void i915_driver_unload(struct drm_device *dev)
drain_workqueue(dev_priv->wq);
intel_guc_fini(dev_priv);
intel_huc_fini(dev_priv);
i915_gem_fini(dev_priv);
intel_fbc_cleanup_cfb(dev_priv);
......@@ -1471,7 +1481,7 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
intel_display_set_init_power(dev_priv, false);
fw_csr = !IS_BROXTON(dev_priv) &&
fw_csr = !IS_GEN9_LP(dev_priv) &&
suspend_to_idle(dev_priv) && dev_priv->csr.dmc_payload;
/*
* In case of firmware assisted context save/restore don't manually
......@@ -1484,7 +1494,7 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
intel_power_domains_suspend(dev_priv);
ret = 0;
if (IS_BROXTON(dev_priv))
if (IS_GEN9_LP(dev_priv))
bxt_enable_dc9(dev_priv);
else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
hsw_enable_pc8(dev_priv);
......@@ -1692,7 +1702,7 @@ static int i915_drm_resume_early(struct drm_device *dev)
intel_uncore_early_sanitize(dev_priv, true);
if (IS_BROXTON(dev_priv)) {
if (IS_GEN9_LP(dev_priv)) {
if (!dev_priv->suspended_to_idle)
gen9_sanitize_dc_state(dev_priv);
bxt_disable_dc9(dev_priv);
......@@ -1702,7 +1712,7 @@ static int i915_drm_resume_early(struct drm_device *dev)
intel_uncore_sanitize(dev_priv);
if (IS_BROXTON(dev_priv) ||
if (IS_GEN9_LP(dev_priv) ||
!(dev_priv->suspended_to_idle && dev_priv->csr.dmc_payload))
intel_power_domains_init_hw(dev_priv, true);
......@@ -1728,25 +1738,9 @@ static int i915_resume_switcheroo(struct drm_device *dev)
return i915_drm_resume(dev);
}
static void disable_engines_irq(struct drm_i915_private *dev_priv)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
/* Ensure irq handler finishes, and not run again. */
disable_irq(dev_priv->drm.irq);
for_each_engine(engine, dev_priv, id)
tasklet_kill(&engine->irq_tasklet);
}
static void enable_engines_irq(struct drm_i915_private *dev_priv)
{
enable_irq(dev_priv->drm.irq);
}
/**
* i915_reset - reset chip after a hang
* @dev: drm device to reset
* @dev_priv: device private to reset
*
* Reset the chip. Useful if a hang is detected. Marks the device as wedged
* on failure.
......@@ -1776,12 +1770,15 @@ void i915_reset(struct drm_i915_private *dev_priv)
error->reset_count++;
pr_notice("drm/i915: Resetting chip after gpu hang\n");
i915_gem_reset_prepare(dev_priv);
disable_irq(dev_priv->drm.irq);
ret = i915_gem_reset_prepare(dev_priv);
if (ret) {
DRM_ERROR("GPU recovery failed\n");
intel_gpu_reset(dev_priv, ALL_ENGINES);
goto error;
}
disable_engines_irq(dev_priv);
ret = intel_gpu_reset(dev_priv, ALL_ENGINES);
enable_engines_irq(dev_priv);
if (ret) {
if (ret != -ENODEV)
DRM_ERROR("Failed to reset chip: %i\n", ret);
......@@ -1816,6 +1813,7 @@ void i915_reset(struct drm_i915_private *dev_priv)
i915_queue_hangcheck(dev_priv);
wakeup:
enable_irq(dev_priv->drm.irq);
wake_up_bit(&error->flags, I915_RESET_IN_PROGRESS);
return;
......@@ -2326,7 +2324,7 @@ static int intel_runtime_suspend(struct device *kdev)
intel_runtime_pm_disable_interrupts(dev_priv);
ret = 0;
if (IS_BROXTON(dev_priv)) {
if (IS_GEN9_LP(dev_priv)) {
bxt_display_core_uninit(dev_priv);
bxt_enable_dc9(dev_priv);
} else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
......@@ -2411,7 +2409,7 @@ static int intel_runtime_resume(struct device *kdev)
if (IS_GEN6(dev_priv))
intel_init_pch_refclk(dev_priv);
if (IS_BROXTON(dev_priv)) {
if (IS_GEN9_LP(dev_priv)) {
bxt_disable_dc9(dev_priv);
bxt_display_core_init(dev_priv, true);
if (dev_priv->csr.dmc_payload &&
......@@ -2549,8 +2547,8 @@ static const struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF_DRV(I915_GEM_MMAP_GTT, i915_gem_mmap_gtt_ioctl, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_GEM_SET_TILING, i915_gem_set_tiling, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_GEM_GET_TILING, i915_gem_get_tiling, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_GEM_SET_TILING, i915_gem_set_tiling_ioctl, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_GEM_GET_TILING, i915_gem_get_tiling_ioctl, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, 0),
DRM_IOCTL_DEF_DRV(I915_GEM_MADVISE, i915_gem_madvise_ioctl, DRM_RENDER_ALLOW),
......
......@@ -78,8 +78,8 @@
#define DRIVER_NAME "i915"
#define DRIVER_DESC "Intel Graphics"
#define DRIVER_DATE "20170109"
#define DRIVER_TIMESTAMP 1483953121
#define DRIVER_DATE "20170123"
#define DRIVER_TIMESTAMP 1485156432
#undef WARN_ON
/* Many gcc seem to no see through this and fall over :( */
......@@ -1069,6 +1069,8 @@ struct intel_fbc {
struct work_struct underrun_work;
struct intel_fbc_state_cache {
struct i915_vma *vma;
struct {
unsigned int mode_flags;
uint32_t hsw_bdw_pixel_rate;
......@@ -1082,15 +1084,14 @@ struct intel_fbc {
} plane;
struct {
u64 ilk_ggtt_offset;
const struct drm_format_info *format;
unsigned int stride;
int fence_reg;
unsigned int tiling_mode;
} fb;
} state_cache;
struct intel_fbc_reg_params {
struct i915_vma *vma;
struct {
enum pipe pipe;
enum plane plane;
......@@ -1098,10 +1099,8 @@ struct intel_fbc {
} crtc;
struct {
u64 ggtt_offset;
const struct drm_format_info *format;
unsigned int stride;
int fence_reg;
} fb;
int cfb_size;
......@@ -1154,6 +1153,9 @@ struct i915_psr {
bool psr2_support;
bool aux_frame_sync;
bool link_standby;
bool y_cord_support;
bool colorimetry_support;
bool alpm;
};
enum intel_pch {
......@@ -1809,6 +1811,7 @@ struct intel_pipe_crc {
enum intel_pipe_crc_source source;
int head, tail;
wait_queue_head_t wq;
int skipped;
};
struct i915_frontbuffer_tracking {
......@@ -2069,6 +2072,7 @@ struct drm_i915_private {
struct intel_gvt *gvt;
struct intel_huc huc;
struct intel_guc guc;
struct intel_csr csr;
......@@ -2843,6 +2847,7 @@ intel_info(const struct drm_i915_private *dev_priv)
#define HAS_GUC(dev_priv) ((dev_priv)->info.has_guc)
#define HAS_GUC_UCODE(dev_priv) (HAS_GUC(dev_priv))
#define HAS_GUC_SCHED(dev_priv) (HAS_GUC(dev_priv))
#define HAS_HUC_UCODE(dev_priv) (HAS_GUC(dev_priv))
#define HAS_RESOURCE_STREAMER(dev_priv) ((dev_priv)->info.has_resource_streamer)
......@@ -3101,10 +3106,10 @@ int i915_gem_throttle_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_set_tiling(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_get_tiling(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_get_tiling_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void i915_gem_init_userptr(struct drm_i915_private *dev_priv);
int i915_gem_userptr_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);
......@@ -3323,7 +3328,7 @@ static inline u32 i915_reset_count(struct i915_gpu_error *error)
return READ_ONCE(error->reset_count);
}
void i915_gem_reset_prepare(struct drm_i915_private *dev_priv);
int i915_gem_reset_prepare(struct drm_i915_private *dev_priv);
void i915_gem_reset_finish(struct drm_i915_private *dev_priv);
void i915_gem_set_wedged(struct drm_i915_private *dev_priv);
void i915_gem_clflush_object(struct drm_i915_gem_object *obj, bool force);
......@@ -3360,11 +3365,6 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj,
int i915_gem_open(struct drm_device *dev, struct drm_file *file);
void i915_gem_release(struct drm_device *dev, struct drm_file *file);
u64 i915_gem_get_ggtt_size(struct drm_i915_private *dev_priv, u64 size,
int tiling_mode);
u64 i915_gem_get_ggtt_alignment(struct drm_i915_private *dev_priv, u64 size,
int tiling_mode, bool fenced);
int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
enum i915_cache_level cache_level);
......@@ -3374,36 +3374,12 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev,
struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *gem_obj, int flags);
struct i915_vma *
i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
struct i915_address_space *vm,
const struct i915_ggtt_view *view);
struct i915_vma *
i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
struct i915_address_space *vm,
const struct i915_ggtt_view *view);
static inline struct i915_hw_ppgtt *
i915_vm_to_ppgtt(struct i915_address_space *vm)
{
return container_of(vm, struct i915_hw_ppgtt, base);
}
static inline struct i915_vma *
i915_gem_object_to_ggtt(struct drm_i915_gem_object *obj,
const struct i915_ggtt_view *view)
{
return i915_gem_obj_to_vma(obj, &to_i915(obj->base.dev)->ggtt.base, view);
}
static inline unsigned long
i915_gem_object_ggtt_offset(struct drm_i915_gem_object *o,
const struct i915_ggtt_view *view)
{
return i915_ggtt_offset(i915_gem_object_to_ggtt(o, view));
}
/* i915_gem_fence_reg.c */
int __must_check i915_vma_get_fence(struct i915_vma *vma);
int __must_check i915_vma_put_fence(struct i915_vma *vma);
......@@ -3471,8 +3447,9 @@ int __must_check i915_gem_evict_something(struct i915_address_space *vm,
unsigned cache_level,
u64 start, u64 end,
unsigned flags);
int __must_check i915_gem_evict_for_vma(struct i915_vma *vma,
unsigned int flags);
int __must_check i915_gem_evict_for_node(struct i915_address_space *vm,
struct drm_mm_node *node,
unsigned int flags);
int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle);
/* belongs in i915_gem_gtt.h */
......@@ -3506,7 +3483,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
/* i915_gem_internal.c */
struct drm_i915_gem_object *
i915_gem_object_create_internal(struct drm_i915_private *dev_priv,
unsigned int size);
phys_addr_t size);
/* i915_gem_shrinker.c */
unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv,
......@@ -3531,6 +3508,11 @@ static inline bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_objec
i915_gem_object_is_tiled(obj);
}
u32 i915_gem_fence_size(struct drm_i915_private *dev_priv, u32 size,
unsigned int tiling, unsigned int stride);
u32 i915_gem_fence_alignment(struct drm_i915_private *dev_priv, u32 size,
unsigned int tiling, unsigned int stride);
/* i915_debugfs.c */
#ifdef CONFIG_DEBUG_FS
int i915_debugfs_register(struct drm_i915_private *dev_priv);
......
......@@ -1696,12 +1696,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
static unsigned int tile_row_pages(struct drm_i915_gem_object *obj)
{
u64 size;
size = i915_gem_object_get_stride(obj);
size *= i915_gem_object_get_tiling(obj) == I915_TILING_Y ? 32 : 8;
return size >> PAGE_SHIFT;
return i915_gem_object_get_tile_row_size(obj) >> PAGE_SHIFT;
}
/**
......@@ -1754,6 +1749,29 @@ int i915_gem_mmap_gtt_version(void)
return 1;
}
static inline struct i915_ggtt_view
compute_partial_view(struct drm_i915_gem_object *obj,
pgoff_t page_offset,
unsigned int chunk)
{
struct i915_ggtt_view view;
if (i915_gem_object_is_tiled(obj))
chunk = roundup(chunk, tile_row_pages(obj));
view.type = I915_GGTT_VIEW_PARTIAL;
view.partial.offset = rounddown(page_offset, chunk);
view.partial.size =
min_t(unsigned int, chunk,
(obj->base.size >> PAGE_SHIFT) - view.partial.offset);
/* If the partial covers the entire object, just create a normal VMA. */
if (chunk >= obj->base.size >> PAGE_SHIFT)
view.type = I915_GGTT_VIEW_NORMAL;
return view;
}
/**
* i915_gem_fault - fault a page into the GTT
* @area: CPU VMA in question
......@@ -1830,26 +1848,9 @@ int i915_gem_fault(struct vm_area_struct *area, struct vm_fault *vmf)
/* Now pin it into the GTT as needed */
vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, flags);
if (IS_ERR(vma)) {
struct i915_ggtt_view view;
unsigned int chunk_size;
/* Use a partial view if it is bigger than available space */
chunk_size = MIN_CHUNK_PAGES;
if (i915_gem_object_is_tiled(obj))
chunk_size = roundup(chunk_size, tile_row_pages(obj));
memset(&view, 0, sizeof(view));
view.type = I915_GGTT_VIEW_PARTIAL;
view.params.partial.offset = rounddown(page_offset, chunk_size);
view.params.partial.size =
min_t(unsigned int, chunk_size,
vma_pages(area) - view.params.partial.offset);
/* If the partial covers the entire object, just create a
* normal VMA.
*/
if (chunk_size >= obj->base.size >> PAGE_SHIFT)
view.type = I915_GGTT_VIEW_NORMAL;
struct i915_ggtt_view view =
compute_partial_view(obj, page_offset, MIN_CHUNK_PAGES);
/* Userspace is now writing through an untracked VMA, abandon
* all hope that the hardware is able to track future writes.
......@@ -1878,7 +1879,7 @@ int i915_gem_fault(struct vm_area_struct *area, struct vm_fault *vmf)
/* Finally, remap it using the new GTT offset */
ret = remap_io_mapping(area,
area->vm_start + (vma->ggtt_view.params.partial.offset << PAGE_SHIFT),
area->vm_start + (vma->ggtt_view.partial.offset << PAGE_SHIFT),
(ggtt->mappable_base + vma->node.start) >> PAGE_SHIFT,
min_t(u64, vma->size, area->vm_end - area->vm_start),
&ggtt->mappable);
......@@ -2021,69 +2022,6 @@ void i915_gem_runtime_suspend(struct drm_i915_private *dev_priv)
}
}
/**
* i915_gem_get_ggtt_size - return required global GTT size for an object
* @dev_priv: i915 device
* @size: object size
* @tiling_mode: tiling mode
*
* Return the required global GTT size for an object, taking into account
* potential fence register mapping.
*/
u64 i915_gem_get_ggtt_size(struct drm_i915_private *dev_priv,
u64 size, int tiling_mode)
{
u64 ggtt_size;
GEM_BUG_ON(size == 0);
if (INTEL_GEN(dev_priv) >= 4 ||
tiling_mode == I915_TILING_NONE)
return size;
/* Previous chips need a power-of-two fence region when tiling */
if (IS_GEN3(dev_priv))
ggtt_size = 1024*1024;
else
ggtt_size = 512*1024;
while (ggtt_size < size)
ggtt_size <<= 1;
return ggtt_size;
}
/**
* i915_gem_get_ggtt_alignment - return required global GTT alignment
* @dev_priv: i915 device
* @size: object size
* @tiling_mode: tiling mode
* @fenced: is fenced alignment required or not
*
* Return the required global GTT alignment for an object, taking into account
* potential fence register mapping.
*/
u64 i915_gem_get_ggtt_alignment(struct drm_i915_private *dev_priv, u64 size,
int tiling_mode, bool fenced)
{
GEM_BUG_ON(size == 0);
/*
* Minimum alignment is 4k (GTT page size), but might be greater
* if a fence register is needed for the object.
*/
if (INTEL_GEN(dev_priv) >= 4 ||
(!fenced && (IS_G33(dev_priv) || IS_PINEVIEW(dev_priv))) ||
tiling_mode == I915_TILING_NONE)
return 4096;
/*
* Previous chips need to be aligned to the size of the smallest
* fence register that can contain the object.
*/
return i915_gem_get_ggtt_size(dev_priv, size, tiling_mode);
}
static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
{
struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
......@@ -2666,13 +2604,52 @@ i915_gem_find_active_request(struct intel_engine_cs *engine)
if (__i915_gem_request_completed(request))
continue;
GEM_BUG_ON(request->engine != engine);
return request;
}
return NULL;
}
static void reset_request(struct drm_i915_gem_request *request)
static bool engine_stalled(struct intel_engine_cs *engine)
{
if (!engine->hangcheck.stalled)
return false;
/* Check for possible seqno movement after hang declaration */
if (engine->hangcheck.seqno != intel_engine_get_seqno(engine)) {
DRM_DEBUG_DRIVER("%s pardoned\n", engine->name);
return false;
}
return true;
}
int i915_gem_reset_prepare(struct drm_i915_private *dev_priv)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
int err = 0;
/* Ensure irq handler finishes, and not run again. */
for_each_engine(engine, dev_priv, id) {
struct drm_i915_gem_request *request;
tasklet_kill(&engine->irq_tasklet);
if (engine_stalled(engine)) {
request = i915_gem_find_active_request(engine);
if (request && request->fence.error == -EIO)
err = -EIO; /* Previous reset failed! */
}
}
i915_gem_revoke_fences(dev_priv);
return err;
}
static void skip_request(struct drm_i915_gem_request *request)
{
void *vaddr = request->ring->vaddr;
u32 head;
......@@ -2687,20 +2664,74 @@ static void reset_request(struct drm_i915_gem_request *request)
head = 0;
}
memset(vaddr + head, 0, request->postfix - head);
dma_fence_set_error(&request->fence, -EIO);
}
void i915_gem_reset_prepare(struct drm_i915_private *dev_priv)
static void engine_skip_context(struct drm_i915_gem_request *request)
{
i915_gem_revoke_fences(dev_priv);
struct intel_engine_cs *engine = request->engine;
struct i915_gem_context *hung_ctx = request->ctx;
struct intel_timeline *timeline;
unsigned long flags;
timeline = i915_gem_context_lookup_timeline(hung_ctx, engine);
spin_lock_irqsave(&engine->timeline->lock, flags);
spin_lock(&timeline->lock);
list_for_each_entry_continue(request, &engine->timeline->requests, link)
if (request->ctx == hung_ctx)
skip_request(request);
list_for_each_entry(request, &timeline->requests, link)
skip_request(request);
spin_unlock(&timeline->lock);
spin_unlock_irqrestore(&engine->timeline->lock, flags);
}
/* Returns true if the request was guilty of hang */
static bool i915_gem_reset_request(struct drm_i915_gem_request *request)
{
/* Read once and return the resolution */
const bool guilty = engine_stalled(request->engine);
/* The guilty request will get skipped on a hung engine.
*
* Users of client default contexts do not rely on logical
* state preserved between batches so it is safe to execute
* queued requests following the hang. Non default contexts
* rely on preserved state, so skipping a batch loses the
* evolution of the state and it needs to be considered corrupted.
* Executing more queued batches on top of corrupted state is
* risky. But we take the risk by trying to advance through
* the queued requests in order to make the client behaviour
* more predictable around resets, by not throwing away random
* amount of batches it has prepared for execution. Sophisticated
* clients can use gem_reset_stats_ioctl and dma fence status
* (exported via sync_file info ioctl on explicit fences) to observe
* when it loses the context state and should rebuild accordingly.
*
* The context ban, and ultimately the client ban, mechanism are safety
* valves if client submission ends up resulting in nothing more than
* subsequent hangs.
*/
if (guilty) {
i915_gem_context_mark_guilty(request->ctx);
skip_request(request);
} else {
i915_gem_context_mark_innocent(request->ctx);
dma_fence_set_error(&request->fence, -EAGAIN);
}
return guilty;
}
static void i915_gem_reset_engine(struct intel_engine_cs *engine)
{
struct drm_i915_gem_request *request;
struct i915_gem_context *hung_ctx;
struct intel_timeline *timeline;
unsigned long flags;
bool ring_hung;
if (engine->irq_seqno_barrier)
engine->irq_seqno_barrier(engine);
......@@ -2709,22 +2740,7 @@ static void i915_gem_reset_engine(struct intel_engine_cs *engine)
if (!request)
return;
hung_ctx = request->ctx;
ring_hung = engine->hangcheck.stalled;
if (engine->hangcheck.seqno != intel_engine_get_seqno(engine)) {
DRM_DEBUG_DRIVER("%s pardoned, was guilty? %s\n",
engine->name,
yesno(ring_hung));
ring_hung = false;
}
if (ring_hung)
i915_gem_context_mark_guilty(hung_ctx);
else
i915_gem_context_mark_innocent(hung_ctx);
if (!ring_hung)
if (!i915_gem_reset_request(request))
return;
DRM_DEBUG_DRIVER("resetting %s to restart from tail of request 0x%x\n",
......@@ -2734,34 +2750,8 @@ static void i915_gem_reset_engine(struct intel_engine_cs *engine)
engine->reset_hw(engine, request);
/* If this context is now banned, skip all of its pending requests. */
if (!i915_gem_context_is_banned(hung_ctx))
return;
/* Users of the default context do not rely on logical state
* preserved between batches. They have to emit full state on
* every batch and so it is safe to execute queued requests following
* the hang.
*
* Other contexts preserve state, now corrupt. We want to skip all
* queued requests that reference the corrupt context.
*/
if (i915_gem_context_is_default(hung_ctx))
return;
timeline = i915_gem_context_lookup_timeline(hung_ctx, engine);
spin_lock_irqsave(&engine->timeline->lock, flags);
spin_lock(&timeline->lock);
list_for_each_entry_continue(request, &engine->timeline->requests, link)
if (request->ctx == hung_ctx)
reset_request(request);
list_for_each_entry(request, &timeline->requests, link)
reset_request(request);
spin_unlock(&timeline->lock);
spin_unlock_irqrestore(&engine->timeline->lock, flags);
if (i915_gem_context_is_banned(request->ctx))
engine_skip_context(request);
}
void i915_gem_reset_finish(struct drm_i915_private *dev_priv)
......@@ -2788,12 +2778,16 @@ void i915_gem_reset_finish(struct drm_i915_private *dev_priv)
static void nop_submit_request(struct drm_i915_gem_request *request)
{
dma_fence_set_error(&request->fence, -EIO);
i915_gem_request_submit(request);
intel_engine_init_global_seqno(request->engine, request->global_seqno);
}
static void i915_gem_cleanup_engine(struct intel_engine_cs *engine)
static void engine_set_wedged(struct intel_engine_cs *engine)
{
struct drm_i915_gem_request *request;
unsigned long flags;
/* We need to be sure that no thread is running the old callback as
* we install the nop handler (otherwise we would submit a request
* to hardware that will never complete). In order to prevent this
......@@ -2802,6 +2796,12 @@ static void i915_gem_cleanup_engine(struct intel_engine_cs *engine)
*/
engine->submit_request = nop_submit_request;
/* Mark all executing requests as skipped */
spin_lock_irqsave(&engine->timeline->lock, flags);
list_for_each_entry(request, &engine->timeline->requests, link)
dma_fence_set_error(&request->fence, -EIO);
spin_unlock_irqrestore(&engine->timeline->lock, flags);
/* Mark all pending requests as complete so that any concurrent
* (lockless) lookup doesn't try and wait upon the request as we
* reset it.
......@@ -2837,7 +2837,7 @@ static int __i915_gem_set_wedged_BKL(void *data)
enum intel_engine_id id;
for_each_engine(engine, i915, id)
i915_gem_cleanup_engine(engine);
engine_set_wedged(engine);
return 0;
}
......@@ -3397,7 +3397,7 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
struct drm_i915_gem_caching *args = data;
struct drm_i915_gem_object *obj;
enum i915_cache_level level;
int ret;
int ret = 0;
switch (args->caching) {
case I915_CACHING_NONE:
......@@ -3422,20 +3422,29 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
ret = i915_mutex_lock_interruptible(dev);
obj = i915_gem_object_lookup(file, args->handle);
if (!obj)
return -ENOENT;
if (obj->cache_level == level)
goto out;
ret = i915_gem_object_wait(obj,
I915_WAIT_INTERRUPTIBLE,
MAX_SCHEDULE_TIMEOUT,
to_rps_client(file));
if (ret)
return ret;
goto out;
obj = i915_gem_object_lookup(file, args->handle);
if (!obj) {
ret = -ENOENT;
goto unlock;
}
ret = i915_mutex_lock_interruptible(dev);
if (ret)
goto out;
ret = i915_gem_object_set_cache_level(obj, level);
i915_gem_object_put(obj);
unlock:
mutex_unlock(&dev->struct_mutex);
out:
i915_gem_object_put(obj);
return ret;
}
......@@ -3485,7 +3494,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
* try to preserve the existing ABI).
*/
vma = ERR_PTR(-ENOSPC);
if (view->type == I915_GGTT_VIEW_NORMAL)
if (!view || view->type == I915_GGTT_VIEW_NORMAL)
vma = i915_gem_object_ggtt_pin(obj, view, 0, alignment,
PIN_MAPPABLE | PIN_NONBLOCK);
if (IS_ERR(vma)) {
......@@ -3544,11 +3553,10 @@ i915_gem_object_unpin_from_display_plane(struct i915_vma *vma)
return;
if (--vma->obj->pin_display == 0)
vma->display_alignment = 0;
vma->display_alignment = I915_GTT_MIN_ALIGNMENT;
/* Bump the LRU to try and avoid premature eviction whilst flipping */
if (!i915_vma_is_active(vma))
list_move_tail(&vma->vm_link, &vma->vm->inactive_list);
i915_gem_object_bump_inactive_ggtt(vma->obj);
i915_vma_unpin(vma);
}
......@@ -3679,8 +3687,8 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
lockdep_assert_held(&obj->base.dev->struct_mutex);
vma = i915_gem_obj_lookup_or_create_vma(obj, vm, view);
if (IS_ERR(vma))
vma = i915_vma_instance(obj, vm, view);
if (unlikely(IS_ERR(vma)))
return vma;
if (i915_vma_misplaced(vma, size, alignment, flags)) {
......@@ -3689,10 +3697,6 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
return ERR_PTR(-ENOSPC);
if (flags & PIN_MAPPABLE) {
u32 fence_size;
fence_size = i915_gem_get_ggtt_size(dev_priv, vma->size,
i915_gem_object_get_tiling(obj));
/* If the required space is larger than the available
* aperture, we will not able to find a slot for the
* object and unbinding the object now will be in
......@@ -3700,7 +3704,7 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
* the object in and out of the Global GTT and
* waste a lot of cycles under the mutex.
*/
if (fence_size > dev_priv->ggtt.mappable_end)
if (vma->fence_size > dev_priv->ggtt.mappable_end)
return ERR_PTR(-E2BIG);
/* If NONBLOCK is set the caller is optimistically
......@@ -3719,7 +3723,7 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
* we could try to minimise harm to others.
*/
if (flags & PIN_NONBLOCK &&
fence_size > dev_priv->ggtt.mappable_end / 2)
vma->fence_size > dev_priv->ggtt.mappable_end / 2)
return ERR_PTR(-ENOSPC);
}
......@@ -4193,7 +4197,8 @@ static void assert_kernel_context_is_current(struct drm_i915_private *dev_priv)
enum intel_engine_id id;
for_each_engine(engine, dev_priv, id)
GEM_BUG_ON(!i915_gem_context_is_kernel(engine->last_retired_context));
GEM_BUG_ON(engine->last_retired_context &&
!i915_gem_context_is_kernel(engine->last_retired_context));
}
int i915_gem_suspend(struct drm_i915_private *dev_priv)
......
......@@ -97,7 +97,7 @@
* part. It should be safe to decrease this, but it's more future proof as is.
*/
#define GEN6_CONTEXT_ALIGN (64<<10)
#define GEN7_CONTEXT_ALIGN 4096
#define GEN7_CONTEXT_ALIGN I915_GTT_MIN_ALIGNMENT
static size_t get_context_alignment(struct drm_i915_private *dev_priv)
{
......@@ -205,27 +205,6 @@ alloc_context_obj(struct drm_i915_private *dev_priv, u64 size)
return obj;
}
static void i915_ppgtt_close(struct i915_address_space *vm)
{
struct list_head *phases[] = {
&vm->active_list,
&vm->inactive_list,
&vm->unbound_list,
NULL,
}, **phase;
GEM_BUG_ON(vm->closed);
vm->closed = true;
for (phase = phases; *phase; phase++) {
struct i915_vma *vma, *vn;
list_for_each_entry_safe(vma, vn, *phase, vm_link)
if (!i915_vma_is_closed(vma))
i915_vma_close(vma);
}
}
static void context_close(struct i915_gem_context *ctx)
{
i915_gem_context_set_closed(ctx);
......@@ -290,7 +269,7 @@ __create_hw_context(struct drm_i915_private *dev_priv,
goto err_out;
}
vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL);
vma = i915_vma_instance(obj, &dev_priv->ggtt.base, NULL);
if (IS_ERR(vma)) {
i915_gem_object_put(obj);
ret = PTR_ERR(vma);
......@@ -341,7 +320,7 @@ __create_hw_context(struct drm_i915_private *dev_priv,
if (HAS_GUC(dev_priv) && i915.enable_guc_loading)
ctx->ggtt_offset_bias = GUC_WOPCM_TOP;
else
ctx->ggtt_offset_bias = 4096;
ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE;
return ctx;
......@@ -456,7 +435,8 @@ int i915_gem_context_init(struct drm_i915_private *dev_priv)
dev_priv->hw_context_size = 0;
} else if (HAS_HW_CONTEXTS(dev_priv)) {
dev_priv->hw_context_size =
round_up(get_context_size(dev_priv), 4096);
round_up(get_context_size(dev_priv),
I915_GTT_PAGE_SIZE);
if (dev_priv->hw_context_size > (1<<20)) {
DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size %d\n",
dev_priv->hw_context_size);
......@@ -897,6 +877,26 @@ int i915_switch_context(struct drm_i915_gem_request *req)
return do_rcs_switch(req);
}
static bool engine_has_kernel_context(struct intel_engine_cs *engine)
{
struct i915_gem_timeline *timeline;
list_for_each_entry(timeline, &engine->i915->gt.timelines, link) {
struct intel_timeline *tl;
if (timeline == &engine->i915->gt.global_timeline)
continue;
tl = &timeline->engine[engine->id];
if (i915_gem_active_peek(&tl->last_request,
&engine->i915->drm.struct_mutex))
return false;
}
return (!engine->last_retired_context ||
i915_gem_context_is_kernel(engine->last_retired_context));
}
int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv)
{
struct intel_engine_cs *engine;
......@@ -905,10 +905,15 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv)
lockdep_assert_held(&dev_priv->drm.struct_mutex);
i915_gem_retire_requests(dev_priv);
for_each_engine(engine, dev_priv, id) {
struct drm_i915_gem_request *req;
int ret;
if (engine_has_kernel_context(engine))
continue;
req = i915_gem_request_alloc(engine, dev_priv->kernel_context);
if (IS_ERR(req))
return PTR_ERR(req);
......
......@@ -231,7 +231,8 @@ i915_gem_evict_something(struct i915_address_space *vm,
/**
* i915_gem_evict_for_vma - Evict vmas to make room for binding a new one
* @target: address space and range to evict for
* @vm: address space to evict from
* @target: range (and color) to evict for
* @flags: additional flags to control the eviction algorithm
*
* This function will try to evict vmas that overlap the target node.
......@@ -239,18 +240,20 @@ i915_gem_evict_something(struct i915_address_space *vm,
* To clarify: This is for freeing up virtual address space, not for freeing
* memory in e.g. the shrinker.
*/
int i915_gem_evict_for_vma(struct i915_vma *target, unsigned int flags)
int i915_gem_evict_for_node(struct i915_address_space *vm,
struct drm_mm_node *target,
unsigned int flags)
{
LIST_HEAD(eviction_list);
struct drm_mm_node *node;
u64 start = target->node.start;
u64 end = start + target->node.size;
u64 start = target->start;
u64 end = start + target->size;
struct i915_vma *vma, *next;
bool check_color;
int ret = 0;
lockdep_assert_held(&target->vm->i915->drm.struct_mutex);
trace_i915_gem_evict_vma(target, flags);
lockdep_assert_held(&vm->i915->drm.struct_mutex);
trace_i915_gem_evict_node(vm, target, flags);
/* Retire before we search the active list. Although we have
* reasonable accuracy in our retirement lists, we may have
......@@ -258,18 +261,18 @@ int i915_gem_evict_for_vma(struct i915_vma *target, unsigned int flags)
* retiring.
*/
if (!(flags & PIN_NONBLOCK))
i915_gem_retire_requests(target->vm->i915);
i915_gem_retire_requests(vm->i915);
check_color = target->vm->mm.color_adjust;
check_color = vm->mm.color_adjust;
if (check_color) {
/* Expand search to cover neighbouring guard pages (or lack!) */
if (start > target->vm->start)
start -= 4096;
if (end < target->vm->start + target->vm->total)
end += 4096;
if (start > vm->start)
start -= I915_GTT_PAGE_SIZE;
if (end < vm->start + vm->total)
end += I915_GTT_PAGE_SIZE;
}
drm_mm_for_each_node_in_range(node, &target->vm->mm, start, end) {
drm_mm_for_each_node_in_range(node, &vm->mm, start, end) {
/* If we find any non-objects (!vma), we cannot evict them */
if (node->color == I915_COLOR_UNEVICTABLE) {
ret = -ENOSPC;
......@@ -285,12 +288,12 @@ int i915_gem_evict_for_vma(struct i915_vma *target, unsigned int flags)
* those as well to make room for our guard pages.
*/
if (check_color) {
if (vma->node.start + vma->node.size == target->node.start) {
if (vma->node.color == target->node.color)
if (vma->node.start + vma->node.size == node->start) {
if (vma->node.color == node->color)
continue;
}
if (vma->node.start == target->node.start + target->node.size) {
if (vma->node.color == target->node.color)
if (vma->node.start == node->start + node->size) {
if (vma->node.color == node->color)
continue;
}
}
......@@ -302,7 +305,7 @@ int i915_gem_evict_for_vma(struct i915_vma *target, unsigned int flags)
}
/* Overlap of objects in the same batch? */
if (i915_vma_is_pinned(vma)) {
if (i915_vma_is_pinned(vma) || !list_empty(&vma->exec_list)) {
ret = -ENOSPC;
if (vma->exec_entry &&
vma->exec_entry->flags & EXEC_OBJECT_PINNED)
......
......@@ -184,7 +184,7 @@ eb_lookup_vmas(struct eb_vmas *eb,
* from the (obj, vm) we don't run the risk of creating
* duplicated vmas for the same vm.
*/
vma = i915_gem_obj_lookup_or_create_vma(obj, vm, NULL);
vma = i915_vma_instance(obj, vm, NULL);
if (unlikely(IS_ERR(vma))) {
DRM_DEBUG("Failed to lookup VMA\n");
ret = PTR_ERR(vma);
......@@ -438,7 +438,7 @@ static void *reloc_iomap(struct drm_i915_gem_object *obj,
memset(&cache->node, 0, sizeof(cache->node));
ret = drm_mm_insert_node_in_range_generic
(&ggtt->base.mm, &cache->node,
4096, 0, I915_COLOR_UNEVICTABLE,
PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE,
0, ggtt->mappable_end,
DRM_MM_SEARCH_DEFAULT,
DRM_MM_CREATE_DEFAULT);
......@@ -851,8 +851,7 @@ eb_vma_misplaced(struct i915_vma *vma)
WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP &&
!i915_vma_is_ggtt(vma));
if (entry->alignment &&
vma->node.start & (entry->alignment - 1))
if (entry->alignment && !IS_ALIGNED(vma->node.start, entry->alignment))
return true;
if (vma->node.size < entry->pad_to_size)
......
......@@ -77,16 +77,17 @@ static void i965_write_fence_reg(struct drm_i915_fence_reg *fence,
val = 0;
if (vma) {
unsigned int tiling = i915_gem_object_get_tiling(vma->obj);
bool is_y_tiled = tiling == I915_TILING_Y;
unsigned int stride = i915_gem_object_get_stride(vma->obj);
u32 row_size = stride * (is_y_tiled ? 32 : 8);
u32 size = rounddown((u32)vma->node.size, row_size);
val = ((vma->node.start + size - 4096) & 0xfffff000) << 32;
val |= vma->node.start & 0xfffff000;
GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma));
GEM_BUG_ON(!IS_ALIGNED(vma->node.start, I965_FENCE_PAGE));
GEM_BUG_ON(!IS_ALIGNED(vma->fence_size, I965_FENCE_PAGE));
GEM_BUG_ON(!IS_ALIGNED(stride, 128));
val = (vma->node.start + vma->fence_size - I965_FENCE_PAGE) << 32;
val |= vma->node.start;
val |= (u64)((stride / 128) - 1) << fence_pitch_shift;
if (is_y_tiled)
if (i915_gem_object_get_tiling(vma->obj) == I915_TILING_Y)
val |= BIT(I965_FENCE_TILING_Y_SHIFT);
val |= I965_FENCE_REG_VALID;
}
......@@ -122,31 +123,24 @@ static void i915_write_fence_reg(struct drm_i915_fence_reg *fence,
unsigned int tiling = i915_gem_object_get_tiling(vma->obj);
bool is_y_tiled = tiling == I915_TILING_Y;
unsigned int stride = i915_gem_object_get_stride(vma->obj);
int pitch_val;
int tile_width;
WARN((vma->node.start & ~I915_FENCE_START_MASK) ||
!is_power_of_2(vma->node.size) ||
(vma->node.start & (vma->node.size - 1)),
"object 0x%08llx [fenceable? %d] not 1M or pot-size (0x%08llx) aligned\n",
vma->node.start,
i915_vma_is_map_and_fenceable(vma),
vma->node.size);
GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma));
GEM_BUG_ON(vma->node.start & ~I915_FENCE_START_MASK);
GEM_BUG_ON(!is_power_of_2(vma->fence_size));
GEM_BUG_ON(!IS_ALIGNED(vma->node.start, vma->fence_size));
if (is_y_tiled && HAS_128_BYTE_Y_TILING(fence->i915))
tile_width = 128;
stride /= 128;
else
tile_width = 512;
/* Note: pitch better be a power of two tile widths */
pitch_val = stride / tile_width;
pitch_val = ffs(pitch_val) - 1;
stride /= 512;
GEM_BUG_ON(!is_power_of_2(stride));
val = vma->node.start;
if (is_y_tiled)
val |= BIT(I830_FENCE_TILING_Y_SHIFT);
val |= I915_FENCE_SIZE_BITS(vma->node.size);
val |= pitch_val << I830_FENCE_PITCH_SHIFT;
val |= I915_FENCE_SIZE_BITS(vma->fence_size);
val |= ilog2(stride) << I830_FENCE_PITCH_SHIFT;
val |= I830_FENCE_REG_VALID;
}
......@@ -166,25 +160,19 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *fence,
val = 0;
if (vma) {
unsigned int tiling = i915_gem_object_get_tiling(vma->obj);
bool is_y_tiled = tiling == I915_TILING_Y;
unsigned int stride = i915_gem_object_get_stride(vma->obj);
u32 pitch_val;
WARN((vma->node.start & ~I830_FENCE_START_MASK) ||
!is_power_of_2(vma->node.size) ||
(vma->node.start & (vma->node.size - 1)),
"object 0x%08llx not 512K or pot-size 0x%08llx aligned\n",
vma->node.start, vma->node.size);
pitch_val = stride / 128;
pitch_val = ffs(pitch_val) - 1;
GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma));
GEM_BUG_ON(vma->node.start & ~I830_FENCE_START_MASK);
GEM_BUG_ON(!is_power_of_2(vma->fence_size));
GEM_BUG_ON(!is_power_of_2(stride / 128));
GEM_BUG_ON(!IS_ALIGNED(vma->node.start, vma->fence_size));
val = vma->node.start;
if (is_y_tiled)
if (i915_gem_object_get_tiling(vma->obj) == I915_TILING_Y)
val |= BIT(I830_FENCE_TILING_Y_SHIFT);
val |= I830_FENCE_SIZE_BITS(vma->node.size);
val |= pitch_val << I830_FENCE_PITCH_SHIFT;
val |= I830_FENCE_SIZE_BITS(vma->fence_size);
val |= ilog2(stride / 128) << I830_FENCE_PITCH_SHIFT;
val |= I830_FENCE_REG_VALID;
}
......
......@@ -30,6 +30,8 @@
struct drm_i915_private;
struct i915_vma;
#define I965_FENCE_PAGE 4096UL
struct drm_i915_fence_reg {
struct list_head link;
struct drm_i915_private *i915;
......
This diff is collapsed.
......@@ -40,6 +40,9 @@
#include "i915_gem_timeline.h"
#include "i915_gem_request.h"
#define I915_GTT_PAGE_SIZE 4096UL
#define I915_GTT_MIN_ALIGNMENT I915_GTT_PAGE_SIZE
#define I915_FENCE_REG_NONE -1
#define I915_MAX_NUM_FENCES 32
/* 32 fences + sign bit for FENCE_REG_NONE */
......@@ -142,34 +145,57 @@ typedef uint64_t gen8_ppgtt_pml4e_t;
struct sg_table;
enum i915_ggtt_view_type {
I915_GGTT_VIEW_NORMAL = 0,
I915_GGTT_VIEW_ROTATED,
I915_GGTT_VIEW_PARTIAL,
};
struct intel_rotation_info {
struct {
struct intel_rotation_plane_info {
/* tiles */
unsigned int width, height, stride, offset;
} plane[2];
} __packed;
static inline void assert_intel_rotation_info_is_packed(void)
{
BUILD_BUG_ON(sizeof(struct intel_rotation_info) != 8*sizeof(unsigned int));
}
struct intel_partial_info {
u64 offset;
unsigned int size;
} __packed;
static inline void assert_intel_partial_info_is_packed(void)
{
BUILD_BUG_ON(sizeof(struct intel_partial_info) != sizeof(u64) + sizeof(unsigned int));
}
enum i915_ggtt_view_type {
I915_GGTT_VIEW_NORMAL = 0,
I915_GGTT_VIEW_ROTATED = sizeof(struct intel_rotation_info),
I915_GGTT_VIEW_PARTIAL = sizeof(struct intel_partial_info),
};
static inline void assert_i915_ggtt_view_type_is_unique(void)
{
/* As we encode the size of each branch inside the union into its type,
* we have to be careful that each branch has a unique size.
*/
switch ((enum i915_ggtt_view_type)0) {
case I915_GGTT_VIEW_NORMAL:
case I915_GGTT_VIEW_PARTIAL:
case I915_GGTT_VIEW_ROTATED:
/* gcc complains if these are identical cases */
break;
}
}
struct i915_ggtt_view {
enum i915_ggtt_view_type type;
union {
struct {
u64 offset;
unsigned int size;
} partial;
/* Members need to contain no holes/padding */
struct intel_partial_info partial;
struct intel_rotation_info rotated;
} params;
};
};
extern const struct i915_ggtt_view i915_ggtt_view_normal;
extern const struct i915_ggtt_view i915_ggtt_view_rotated;
enum i915_cache_level;
struct i915_vma;
......@@ -333,6 +359,7 @@ struct i915_ggtt {
/** "Graphics Stolen Memory" holds the global PTEs */
void __iomem *gsm;
void (*invalidate)(struct drm_i915_private *dev_priv);
bool do_idle_maps;
......@@ -501,6 +528,8 @@ i915_vm_to_ggtt(struct i915_address_space *vm)
int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv);
int i915_ggtt_init_hw(struct drm_i915_private *dev_priv);
int i915_ggtt_enable_hw(struct drm_i915_private *dev_priv);
void i915_ggtt_enable_guc(struct drm_i915_private *i915);
void i915_ggtt_disable_guc(struct drm_i915_private *i915);
int i915_gem_init_ggtt(struct drm_i915_private *dev_priv);
void i915_ggtt_cleanup_hw(struct drm_i915_private *dev_priv);
......@@ -509,6 +538,7 @@ void i915_ppgtt_release(struct kref *kref);
struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_i915_private *dev_priv,
struct drm_i915_file_private *fpriv,
const char *name);
void i915_ppgtt_close(struct i915_address_space *vm);
static inline void i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt)
{
if (ppgtt)
......@@ -529,6 +559,16 @@ int __must_check i915_gem_gtt_prepare_pages(struct drm_i915_gem_object *obj,
void i915_gem_gtt_finish_pages(struct drm_i915_gem_object *obj,
struct sg_table *pages);
int i915_gem_gtt_reserve(struct i915_address_space *vm,
struct drm_mm_node *node,
u64 size, u64 offset, unsigned long color,
unsigned int flags);
int i915_gem_gtt_insert(struct i915_address_space *vm,
struct drm_mm_node *node,
u64 size, u64 alignment, unsigned long color,
u64 start, u64 end, unsigned int flags);
/* Flags used by pin/bind&friends. */
#define PIN_NONBLOCK BIT(0)
#define PIN_MAPPABLE BIT(1)
......@@ -543,6 +583,6 @@ void i915_gem_gtt_finish_pages(struct drm_i915_gem_object *obj,
#define PIN_HIGH BIT(9)
#define PIN_OFFSET_BIAS BIT(10)
#define PIN_OFFSET_FIXED BIT(11)
#define PIN_OFFSET_MASK (~4095)
#define PIN_OFFSET_MASK (-I915_GTT_PAGE_SIZE)
#endif
......@@ -151,10 +151,16 @@ static const struct drm_i915_gem_object_ops i915_gem_object_internal_ops = {
*/
struct drm_i915_gem_object *
i915_gem_object_create_internal(struct drm_i915_private *i915,
unsigned int size)
phys_addr_t size)
{
struct drm_i915_gem_object *obj;
GEM_BUG_ON(!size);
GEM_BUG_ON(!IS_ALIGNED(size, PAGE_SIZE));
if (overflows_type(size, obj->base.size))
return ERR_PTR(-E2BIG);
obj = i915_gem_object_alloc(i915);
if (!obj)
return ERR_PTR(-ENOMEM);
......
......@@ -317,6 +317,29 @@ i915_gem_object_get_stride(struct drm_i915_gem_object *obj)
return obj->tiling_and_stride & STRIDE_MASK;
}
static inline unsigned int
i915_gem_tile_height(unsigned int tiling)
{
GEM_BUG_ON(!tiling);
return tiling == I915_TILING_Y ? 32 : 8;
}
static inline unsigned int
i915_gem_object_get_tile_height(struct drm_i915_gem_object *obj)
{
return i915_gem_tile_height(i915_gem_object_get_tiling(obj));
}
static inline unsigned int
i915_gem_object_get_tile_row_size(struct drm_i915_gem_object *obj)
{
return (i915_gem_object_get_stride(obj) *
i915_gem_object_get_tile_height(obj));
}
int i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
unsigned int tiling, unsigned int stride);
static inline struct intel_engine_cs *
i915_gem_object_last_write_engine(struct drm_i915_gem_object *obj)
{
......
......@@ -187,20 +187,20 @@ int i915_gem_render_state_init(struct intel_engine_cs *engine)
if (!rodata)
return 0;
if (rodata->batch_items * 4 > 4096)
if (rodata->batch_items * 4 > PAGE_SIZE)
return -EINVAL;
so = kmalloc(sizeof(*so), GFP_KERNEL);
if (!so)
return -ENOMEM;
obj = i915_gem_object_create_internal(engine->i915, 4096);
obj = i915_gem_object_create_internal(engine->i915, PAGE_SIZE);
if (IS_ERR(obj)) {
ret = PTR_ERR(obj);
goto err_free;
}
so->vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL);
so->vma = i915_vma_instance(obj, &engine->i915->ggtt.base, NULL);
if (IS_ERR(so->vma)) {
ret = PTR_ERR(so->vma);
goto err_obj;
......
......@@ -307,26 +307,6 @@ void i915_gem_request_retire_upto(struct drm_i915_gem_request *req)
} while (tmp != req);
}
static int i915_gem_check_wedge(struct drm_i915_private *dev_priv)
{
struct i915_gpu_error *error = &dev_priv->gpu_error;
if (i915_terminally_wedged(error))
return -EIO;
if (i915_reset_in_progress(error)) {
/* Non-interruptible callers can't handle -EAGAIN, hence return
* -EIO unconditionally for these.
*/
if (!dev_priv->mm.interruptible)
return -EIO;
return -EAGAIN;
}
return 0;
}
static int i915_gem_init_global_seqno(struct drm_i915_private *i915, u32 seqno)
{
struct i915_gem_timeline *timeline = &i915->gt.global_timeline;
......@@ -521,12 +501,10 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
lockdep_assert_held(&dev_priv->drm.struct_mutex);
/* ABI: Before userspace accesses the GPU (e.g. execbuffer), report
* EIO if the GPU is already wedged, or EAGAIN to drop the struct_mutex
* and restart.
* EIO if the GPU is already wedged.
*/
ret = i915_gem_check_wedge(dev_priv);
if (ret)
return ERR_PTR(ret);
if (i915_terminally_wedged(&dev_priv->gpu_error))
return ERR_PTR(-EIO);
/* Pinning the contexts may generate requests in order to acquire
* GGTT space, so do this first before we reserve a seqno for
......@@ -851,6 +829,13 @@ void __i915_add_request(struct drm_i915_gem_request *request, bool flush_caches)
lockdep_assert_held(&request->i915->drm.struct_mutex);
trace_i915_gem_request_add(request);
/* Make sure that no request gazumped us - if it was allocated after
* our i915_gem_request_alloc() and called __i915_add_request() before
* us, the timeline will hold its seqno which is later than ours.
*/
GEM_BUG_ON(i915_seqno_passed(timeline->last_submitted_seqno,
request->fence.seqno));
/*
* To ensure that this call will not fail, space for its emissions
* should already have been reserved in the ring buffer. Let the ring
......
......@@ -647,8 +647,9 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
stolen_offset, gtt_offset, size);
/* KISS and expect everything to be page-aligned */
if (WARN_ON(size == 0) || WARN_ON(size & 4095) ||
WARN_ON(stolen_offset & 4095))
if (WARN_ON(size == 0) ||
WARN_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE)) ||
WARN_ON(!IS_ALIGNED(stolen_offset, I915_GTT_MIN_ALIGNMENT)))
return NULL;
stolen = kzalloc(sizeof(*stolen), GFP_KERNEL);
......@@ -682,7 +683,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
if (ret)
goto err;
vma = i915_gem_obj_lookup_or_create_vma(obj, &ggtt->base, NULL);
vma = i915_vma_instance(obj, &ggtt->base, NULL);
if (IS_ERR(vma)) {
ret = PTR_ERR(vma);
goto err_pages;
......@@ -693,15 +694,16 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
* setting up the GTT space. The actual reservation will occur
* later.
*/
vma->node.start = gtt_offset;
vma->node.size = size;
ret = drm_mm_reserve_node(&ggtt->base.mm, &vma->node);
ret = i915_gem_gtt_reserve(&ggtt->base, &vma->node,
size, gtt_offset, obj->cache_level,
0);
if (ret) {
DRM_DEBUG_KMS("failed to allocate stolen GTT space\n");
goto err_pages;
}
GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
vma->pages = obj->mm.pages;
vma->flags |= I915_VMA_GLOBAL_BIND;
__i915_vma_set_map_and_fenceable(vma);
......
......@@ -34,8 +34,8 @@
/**
* DOC: buffer object tiling
*
* i915_gem_set_tiling() and i915_gem_get_tiling() is the userspace interface to
* declare fence register requirements.
* i915_gem_set_tiling_ioctl() and i915_gem_get_tiling_ioctl() is the userspace
* interface to declare fence register requirements.
*
* In principle GEM doesn't care at all about the internal data layout of an
* object, and hence it also doesn't care about tiling or swizzling. There's two
......@@ -58,86 +58,147 @@
* invovlement.
*/
/**
* i915_gem_fence_size - required global GTT size for a fence
* @i915: i915 device
* @size: object size
* @tiling: tiling mode
* @stride: tiling stride
*
* Return the required global GTT size for a fence (view of a tiled object),
* taking into account potential fence register mapping.
*/
u32 i915_gem_fence_size(struct drm_i915_private *i915,
u32 size, unsigned int tiling, unsigned int stride)
{
u32 ggtt_size;
GEM_BUG_ON(!size);
if (tiling == I915_TILING_NONE)
return size;
GEM_BUG_ON(!stride);
if (INTEL_GEN(i915) >= 4) {
stride *= i915_gem_tile_height(tiling);
GEM_BUG_ON(!IS_ALIGNED(stride, I965_FENCE_PAGE));
return roundup(size, stride);
}
/* Previous chips need a power-of-two fence region when tiling */
if (IS_GEN3(i915))
ggtt_size = 1024*1024;
else
ggtt_size = 512*1024;
while (ggtt_size < size)
ggtt_size <<= 1;
return ggtt_size;
}
/**
* i915_gem_fence_alignment - required global GTT alignment for a fence
* @i915: i915 device
* @size: object size
* @tiling: tiling mode
* @stride: tiling stride
*
* Return the required global GTT alignment for a fence (a view of a tiled
* object), taking into account potential fence register mapping.
*/
u32 i915_gem_fence_alignment(struct drm_i915_private *i915, u32 size,
unsigned int tiling, unsigned int stride)
{
GEM_BUG_ON(!size);
/*
* Minimum alignment is 4k (GTT page size), but might be greater
* if a fence register is needed for the object.
*/
if (tiling == I915_TILING_NONE)
return I915_GTT_MIN_ALIGNMENT;
if (INTEL_GEN(i915) >= 4)
return I965_FENCE_PAGE;
/*
* Previous chips need to be aligned to the size of the smallest
* fence register that can contain the object.
*/
return i915_gem_fence_size(i915, size, tiling, stride);
}
/* Check pitch constriants for all chips & tiling formats */
static bool
i915_tiling_ok(struct drm_i915_private *dev_priv,
int stride, int size, int tiling_mode)
i915_tiling_ok(struct drm_i915_gem_object *obj,
unsigned int tiling, unsigned int stride)
{
int tile_width;
struct drm_i915_private *i915 = to_i915(obj->base.dev);
unsigned int tile_width;
/* Linear is always fine */
if (tiling_mode == I915_TILING_NONE)
if (tiling == I915_TILING_NONE)
return true;
if (tiling_mode > I915_TILING_LAST)
if (tiling > I915_TILING_LAST)
return false;
if (IS_GEN2(dev_priv) ||
(tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev_priv)))
tile_width = 128;
else
tile_width = 512;
/* check maximum stride & object size */
/* i965+ stores the end address of the gtt mapping in the fence
* reg, so dont bother to check the size */
if (INTEL_GEN(dev_priv) >= 7) {
if (INTEL_GEN(i915) >= 7) {
if (stride / 128 > GEN7_FENCE_MAX_PITCH_VAL)
return false;
} else if (INTEL_GEN(dev_priv) >= 4) {
} else if (INTEL_GEN(i915) >= 4) {
if (stride / 128 > I965_FENCE_MAX_PITCH_VAL)
return false;
} else {
if (stride > 8192)
return false;
if (IS_GEN3(dev_priv)) {
if (size > I830_FENCE_MAX_SIZE_VAL << 20)
if (IS_GEN3(i915)) {
if (obj->base.size > I830_FENCE_MAX_SIZE_VAL << 20)
return false;
} else {
if (size > I830_FENCE_MAX_SIZE_VAL << 19)
if (obj->base.size > I830_FENCE_MAX_SIZE_VAL << 19)
return false;
}
}
if (stride < tile_width)
if (IS_GEN2(i915) ||
(tiling == I915_TILING_Y && HAS_128_BYTE_Y_TILING(i915)))
tile_width = 128;
else
tile_width = 512;
if (!IS_ALIGNED(stride, tile_width))
return false;
/* 965+ just needs multiples of tile width */
if (INTEL_GEN(dev_priv) >= 4) {
if (stride & (tile_width - 1))
return false;
if (INTEL_GEN(i915) >= 4)
return true;
}
/* Pre-965 needs power of two tile widths */
if (stride & (stride - 1))
return false;
return true;
return is_power_of_2(stride);
}
static bool i915_vma_fence_prepare(struct i915_vma *vma, int tiling_mode)
static bool i915_vma_fence_prepare(struct i915_vma *vma,
int tiling_mode, unsigned int stride)
{
struct drm_i915_private *dev_priv = vma->vm->i915;
u32 size;
struct drm_i915_private *i915 = vma->vm->i915;
u32 size, alignment;
if (!i915_vma_is_map_and_fenceable(vma))
return true;
if (INTEL_GEN(dev_priv) == 3) {
if (vma->node.start & ~I915_FENCE_START_MASK)
return false;
} else {
if (vma->node.start & ~I830_FENCE_START_MASK)
return false;
}
size = i915_gem_get_ggtt_size(dev_priv, vma->size, tiling_mode);
size = i915_gem_fence_size(i915, vma->size, tiling_mode, stride);
if (vma->node.size < size)
return false;
if (vma->node.start & (size - 1))
alignment = i915_gem_fence_alignment(i915, vma->size, tiling_mode, stride);
if (!IS_ALIGNED(vma->node.start, alignment))
return false;
return true;
......@@ -145,20 +206,20 @@ static bool i915_vma_fence_prepare(struct i915_vma *vma, int tiling_mode)
/* Make the current GTT allocation valid for the change in tiling. */
static int
i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, int tiling_mode)
i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj,
int tiling_mode, unsigned int stride)
{
struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
struct i915_vma *vma;
int ret;
if (tiling_mode == I915_TILING_NONE)
return 0;
if (INTEL_GEN(dev_priv) >= 4)
return 0;
list_for_each_entry(vma, &obj->vma_list, obj_link) {
if (i915_vma_fence_prepare(vma, tiling_mode))
if (!i915_vma_is_ggtt(vma))
break;
if (i915_vma_fence_prepare(vma, tiling_mode, stride))
continue;
ret = i915_vma_unbind(vma);
......@@ -169,8 +230,100 @@ i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, int tiling_mode)
return 0;
}
int
i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
unsigned int tiling, unsigned int stride)
{
struct drm_i915_private *i915 = to_i915(obj->base.dev);
struct i915_vma *vma;
int err;
/* Make sure we don't cross-contaminate obj->tiling_and_stride */
BUILD_BUG_ON(I915_TILING_LAST & STRIDE_MASK);
GEM_BUG_ON(!i915_tiling_ok(obj, tiling, stride));
GEM_BUG_ON(!stride ^ (tiling == I915_TILING_NONE));
lockdep_assert_held(&i915->drm.struct_mutex);
if ((tiling | stride) == obj->tiling_and_stride)
return 0;
if (obj->framebuffer_references)
return -EBUSY;
/* We need to rebind the object if its current allocation
* no longer meets the alignment restrictions for its new
* tiling mode. Otherwise we can just leave it alone, but
* need to ensure that any fence register is updated before
* the next fenced (either through the GTT or by the BLT unit
* on older GPUs) access.
*
* After updating the tiling parameters, we then flag whether
* we need to update an associated fence register. Note this
* has to also include the unfenced register the GPU uses
* whilst executing a fenced command for an untiled object.
*/
err = i915_gem_object_fence_prepare(obj, tiling, stride);
if (err)
return err;
/* If the memory has unknown (i.e. varying) swizzling, we pin the
* pages to prevent them being swapped out and causing corruption
* due to the change in swizzling.
*/
mutex_lock(&obj->mm.lock);
if (obj->mm.pages &&
obj->mm.madv == I915_MADV_WILLNEED &&
i915->quirks & QUIRK_PIN_SWIZZLED_PAGES) {
if (tiling == I915_TILING_NONE) {
GEM_BUG_ON(!obj->mm.quirked);
__i915_gem_object_unpin_pages(obj);
obj->mm.quirked = false;
}
if (!i915_gem_object_is_tiled(obj)) {
GEM_BUG_ON(!obj->mm.quirked);
__i915_gem_object_pin_pages(obj);
obj->mm.quirked = true;
}
}
mutex_unlock(&obj->mm.lock);
list_for_each_entry(vma, &obj->vma_list, obj_link) {
if (!i915_vma_is_ggtt(vma))
break;
vma->fence_size =
i915_gem_fence_size(i915, vma->size, tiling, stride);
vma->fence_alignment =
i915_gem_fence_alignment(i915,
vma->size, tiling, stride);
if (vma->fence)
vma->fence->dirty = true;
}
obj->tiling_and_stride = tiling | stride;
/* Force the fence to be reacquired for GTT access */
i915_gem_release_mmap(obj);
/* Try to preallocate memory required to save swizzling on put-pages */
if (i915_gem_object_needs_bit17_swizzle(obj)) {
if (!obj->bit_17) {
obj->bit_17 = kcalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT),
sizeof(long), GFP_KERNEL);
}
} else {
kfree(obj->bit_17);
obj->bit_17 = NULL;
}
return 0;
}
/**
* i915_gem_set_tiling - IOCTL handler to set tiling mode
* i915_gem_set_tiling_ioctl - IOCTL handler to set tiling mode
* @dev: DRM device
* @data: data pointer for the ioctl
* @file: DRM file for the ioctl call
......@@ -184,30 +337,19 @@ i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, int tiling_mode)
* Zero on success, negative errno on failure.
*/
int
i915_gem_set_tiling(struct drm_device *dev, void *data,
struct drm_file *file)
i915_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_i915_gem_set_tiling *args = data;
struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_i915_gem_object *obj;
int err = 0;
/* Make sure we don't cross-contaminate obj->tiling_and_stride */
BUILD_BUG_ON(I915_TILING_LAST & STRIDE_MASK);
int err;
obj = i915_gem_object_lookup(file, args->handle);
if (!obj)
return -ENOENT;
if (!i915_tiling_ok(dev_priv,
args->stride, obj->base.size, args->tiling_mode)) {
i915_gem_object_put(obj);
return -EINVAL;
}
mutex_lock(&dev->struct_mutex);
if (obj->pin_display || obj->framebuffer_references) {
err = -EBUSY;
if (!i915_tiling_ok(obj, args->tiling_mode, args->stride)) {
err = -EINVAL;
goto err;
}
......@@ -216,9 +358,9 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
args->stride = 0;
} else {
if (args->tiling_mode == I915_TILING_X)
args->swizzle_mode = dev_priv->mm.bit_6_swizzle_x;
args->swizzle_mode = to_i915(dev)->mm.bit_6_swizzle_x;
else
args->swizzle_mode = dev_priv->mm.bit_6_swizzle_y;
args->swizzle_mode = to_i915(dev)->mm.bit_6_swizzle_y;
/* Hide bit 17 swizzling from the user. This prevents old Mesa
* from aborting the application on sw fallbacks to bit 17,
......@@ -240,79 +382,24 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
}
}
if (args->tiling_mode != i915_gem_object_get_tiling(obj) ||
args->stride != i915_gem_object_get_stride(obj)) {
/* We need to rebind the object if its current allocation
* no longer meets the alignment restrictions for its new
* tiling mode. Otherwise we can just leave it alone, but
* need to ensure that any fence register is updated before
* the next fenced (either through the GTT or by the BLT unit
* on older GPUs) access.
*
* After updating the tiling parameters, we then flag whether
* we need to update an associated fence register. Note this
* has to also include the unfenced register the GPU uses
* whilst executing a fenced command for an untiled object.
*/
err = mutex_lock_interruptible(&dev->struct_mutex);
if (err)
goto err;
err = i915_gem_object_fence_prepare(obj, args->tiling_mode);
if (!err) {
struct i915_vma *vma;
mutex_lock(&obj->mm.lock);
if (obj->mm.pages &&
obj->mm.madv == I915_MADV_WILLNEED &&
dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES) {
if (args->tiling_mode == I915_TILING_NONE) {
GEM_BUG_ON(!obj->mm.quirked);
__i915_gem_object_unpin_pages(obj);
obj->mm.quirked = false;
}
if (!i915_gem_object_is_tiled(obj)) {
GEM_BUG_ON(!obj->mm.quirked);
__i915_gem_object_pin_pages(obj);
obj->mm.quirked = true;
}
}
mutex_unlock(&obj->mm.lock);
list_for_each_entry(vma, &obj->vma_list, obj_link) {
if (!vma->fence)
continue;
vma->fence->dirty = true;
}
obj->tiling_and_stride =
args->stride | args->tiling_mode;
/* Force the fence to be reacquired for GTT access */
i915_gem_release_mmap(obj);
}
}
/* we have to maintain this existing ABI... */
err = i915_gem_object_set_tiling(obj, args->tiling_mode, args->stride);
mutex_unlock(&dev->struct_mutex);
/* We have to maintain this existing ABI... */
args->stride = i915_gem_object_get_stride(obj);
args->tiling_mode = i915_gem_object_get_tiling(obj);
/* Try to preallocate memory required to save swizzling on put-pages */
if (i915_gem_object_needs_bit17_swizzle(obj)) {
if (obj->bit_17 == NULL) {
obj->bit_17 = kcalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT),
sizeof(long), GFP_KERNEL);
}
} else {
kfree(obj->bit_17);
obj->bit_17 = NULL;
}
err:
i915_gem_object_put(obj);
mutex_unlock(&dev->struct_mutex);
return err;
}
/**
* i915_gem_get_tiling - IOCTL handler to get tiling mode
* i915_gem_get_tiling_ioctl - IOCTL handler to get tiling mode
* @dev: DRM device
* @data: data pointer for the ioctl
* @file: DRM file for the ioctl call
......@@ -325,8 +412,8 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
* Zero on success, negative errno on failure.
*/
int
i915_gem_get_tiling(struct drm_device *dev, void *data,
struct drm_file *file)
i915_gem_get_tiling_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_i915_gem_get_tiling *args = data;
struct drm_i915_private *dev_priv = to_i915(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