gvt.c 8.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
 *
 * 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 (including the next
 * paragraph) 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.
22 23 24 25 26 27 28 29 30
 *
 * Authors:
 *    Kevin Tian <kevin.tian@intel.com>
 *    Eddie Dong <eddie.dong@intel.com>
 *
 * Contributors:
 *    Niu Bing <bing.niu@intel.com>
 *    Zhi Wang <zhi.a.wang@intel.com>
 *
31 32 33 34
 */

#include <linux/types.h>
#include <xen/xen.h>
35
#include <linux/kthread.h>
36 37

#include "i915_drv.h"
38
#include "gvt.h"
39 40 41 42 43 44 45 46

struct intel_gvt_host intel_gvt_host;

static const char * const supported_hypervisors[] = {
	[INTEL_GVT_HYPERVISOR_XEN] = "XEN",
	[INTEL_GVT_HYPERVISOR_KVM] = "KVM",
};

47
static const struct intel_gvt_ops intel_gvt_ops = {
48 49
	.emulate_cfg_read = intel_vgpu_emulate_cfg_read,
	.emulate_cfg_write = intel_vgpu_emulate_cfg_write,
Z
Zhi Wang 已提交
50 51
	.emulate_mmio_read = intel_vgpu_emulate_mmio_read,
	.emulate_mmio_write = intel_vgpu_emulate_mmio_write,
52 53 54
	.vgpu_create = intel_gvt_create_vgpu,
	.vgpu_destroy = intel_gvt_destroy_vgpu,
	.vgpu_reset = intel_gvt_reset_vgpu,
55 56
};

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
/**
 * intel_gvt_init_host - Load MPT modules and detect if we're running in host
 * @gvt: intel gvt device
 *
 * This function is called at the driver loading stage. If failed to find a
 * loadable MPT module or detect currently we're running in a VM, then GVT-g
 * will be disabled
 *
 * Returns:
 * Zero on success, negative error code if failed.
 *
 */
int intel_gvt_init_host(void)
{
	if (intel_gvt_host.initialized)
		return 0;

	/* Xen DOM U */
	if (xen_domain() && !xen_initial_domain())
		return -ENODEV;

	/* Try to load MPT modules for hypervisors */
	if (xen_initial_domain()) {
		/* In Xen dom0 */
		intel_gvt_host.mpt = try_then_request_module(
				symbol_get(xengt_mpt), "xengt");
		intel_gvt_host.hypervisor_type = INTEL_GVT_HYPERVISOR_XEN;
	} else {
J
Jike Song 已提交
85
#if IS_ENABLED(CONFIG_DRM_I915_GVT_KVMGT)
86 87
		/* not in Xen. Try KVMGT */
		intel_gvt_host.mpt = try_then_request_module(
J
Jike Song 已提交
88
				symbol_get(kvmgt_mpt), "kvmgt");
89
		intel_gvt_host.hypervisor_type = INTEL_GVT_HYPERVISOR_KVM;
J
Jike Song 已提交
90
#endif
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
	}

	/* Fail to load MPT modules - bail out */
	if (!intel_gvt_host.mpt)
		return -EINVAL;

	gvt_dbg_core("Running with hypervisor %s in host mode\n",
			supported_hypervisors[intel_gvt_host.hypervisor_type]);

	intel_gvt_host.initialized = true;
	return 0;
}

static void init_device_info(struct intel_gvt *gvt)
{
106
	struct intel_gvt_device_info *info = &gvt->device_info;
107
	struct pci_dev *pdev = gvt->dev_priv->drm.pdev;
108 109 110

	if (IS_BROADWELL(gvt->dev_priv) || IS_SKYLAKE(gvt->dev_priv)) {
		info->max_support_vgpus = 8;
111
		info->cfg_space_size = 256;
112
		info->mmio_size = 2 * 1024 * 1024;
113
		info->mmio_bar = 0;
114 115 116
		info->gtt_start_offset = 8 * 1024 * 1024;
		info->gtt_entry_size = 8;
		info->gtt_entry_size_shift = 3;
Z
Zhi Wang 已提交
117 118
		info->gmadr_bytes_in_cmd = 8;
		info->max_surface_size = 36 * 1024 * 1024;
119
	}
120
	info->msi_cap_offset = pdev->msi_cap;
121 122
}

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
static int gvt_service_thread(void *data)
{
	struct intel_gvt *gvt = (struct intel_gvt *)data;
	int ret;

	gvt_dbg_core("service thread start\n");

	while (!kthread_should_stop()) {
		ret = wait_event_interruptible(gvt->service_thread_wq,
				kthread_should_stop() || gvt->service_request);

		if (kthread_should_stop())
			break;

		if (WARN_ONCE(ret, "service thread is waken up by signal.\n"))
			continue;

		if (test_and_clear_bit(INTEL_GVT_REQUEST_EMULATE_VBLANK,
					(void *)&gvt->service_request)) {
			mutex_lock(&gvt->lock);
			intel_gvt_emulate_vblank(gvt);
			mutex_unlock(&gvt->lock);
		}
	}

	return 0;
}

static void clean_service_thread(struct intel_gvt *gvt)
{
	kthread_stop(gvt->service_thread);
}

static int init_service_thread(struct intel_gvt *gvt)
{
	init_waitqueue_head(&gvt->service_thread_wq);

	gvt->service_thread = kthread_run(gvt_service_thread,
			gvt, "gvt_service_thread");
	if (IS_ERR(gvt->service_thread)) {
		gvt_err("fail to start service thread.\n");
		return PTR_ERR(gvt->service_thread);
	}
	return 0;
}

169 170 171 172 173 174 175 176 177 178
/**
 * intel_gvt_clean_device - clean a GVT device
 * @gvt: intel gvt device
 *
 * This function is called at the driver unloading stage, to free the
 * resources owned by a GVT device.
 *
 */
void intel_gvt_clean_device(struct drm_i915_private *dev_priv)
{
179
	struct intel_gvt *gvt = to_gvt(dev_priv);
180

181
	if (WARN_ON(!gvt))
182 183
		return;

184
	clean_service_thread(gvt);
Z
Zhi Wang 已提交
185
	intel_gvt_clean_cmd_parser(gvt);
186
	intel_gvt_clean_sched_policy(gvt);
Z
Zhi Wang 已提交
187
	intel_gvt_clean_workload_scheduler(gvt);
188
	intel_gvt_clean_opregion(gvt);
189
	intel_gvt_clean_gtt(gvt);
190
	intel_gvt_clean_irq(gvt);
191
	intel_gvt_clean_mmio_info(gvt);
192
	intel_gvt_free_firmware(gvt);
193

194
	intel_gvt_hypervisor_host_exit(&dev_priv->drm.pdev->dev, gvt);
195 196
	intel_gvt_clean_vgpu_types(gvt);

197 198
	idr_destroy(&gvt->vgpu_idr);

199 200
	kfree(dev_priv->gvt);
	dev_priv->gvt = NULL;
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
}

/**
 * intel_gvt_init_device - initialize a GVT device
 * @dev_priv: drm i915 private data
 *
 * This function is called at the initialization stage, to initialize
 * necessary GVT components.
 *
 * Returns:
 * Zero on success, negative error code if failed.
 *
 */
int intel_gvt_init_device(struct drm_i915_private *dev_priv)
{
216
	struct intel_gvt *gvt;
217 218
	int ret;

219 220 221 222 223 224 225
	/*
	 * Cannot initialize GVT device without intel_gvt_host gets
	 * initialized first.
	 */
	if (WARN_ON(!intel_gvt_host.initialized))
		return -EINVAL;

226
	if (WARN_ON(dev_priv->gvt))
227 228
		return -EEXIST;

229 230 231 232
	gvt = kzalloc(sizeof(struct intel_gvt), GFP_KERNEL);
	if (!gvt)
		return -ENOMEM;

233 234
	gvt_dbg_core("init gvt device\n");

235 236
	idr_init(&gvt->vgpu_idr);

237 238 239
	mutex_init(&gvt->lock);
	gvt->dev_priv = dev_priv;

240
	init_device_info(gvt);
241 242 243

	ret = intel_gvt_setup_mmio_info(gvt);
	if (ret)
244
		goto out_clean_idr;
245

246 247 248 249
	ret = intel_gvt_load_firmware(gvt);
	if (ret)
		goto out_clean_mmio_info;

250 251 252 253
	ret = intel_gvt_init_irq(gvt);
	if (ret)
		goto out_free_firmware;

254 255 256 257
	ret = intel_gvt_init_gtt(gvt);
	if (ret)
		goto out_clean_irq;

258 259 260 261
	ret = intel_gvt_init_opregion(gvt);
	if (ret)
		goto out_clean_gtt;

Z
Zhi Wang 已提交
262
	ret = intel_gvt_init_workload_scheduler(gvt);
263 264 265
	if (ret)
		goto out_clean_opregion;

266
	ret = intel_gvt_init_sched_policy(gvt);
Z
Zhi Wang 已提交
267 268 269
	if (ret)
		goto out_clean_workload_scheduler;

Z
Zhi Wang 已提交
270
	ret = intel_gvt_init_cmd_parser(gvt);
271 272 273
	if (ret)
		goto out_clean_sched_policy;

Z
Zhi Wang 已提交
274 275 276 277
	ret = init_service_thread(gvt);
	if (ret)
		goto out_clean_cmd_parser;

278 279 280 281
	ret = intel_gvt_init_vgpu_types(gvt);
	if (ret)
		goto out_clean_thread;

282
	ret = intel_gvt_hypervisor_host_init(&dev_priv->drm.pdev->dev, gvt,
283
				&intel_gvt_ops);
284 285 286 287
	if (ret) {
		gvt_err("failed to register gvt-g host device: %d\n", ret);
		goto out_clean_types;
	}
288 289

	gvt_dbg_core("gvt device initialization is done\n");
290
	dev_priv->gvt = gvt;
291
	return 0;
292

293 294
out_clean_types:
	intel_gvt_clean_vgpu_types(gvt);
295 296
out_clean_thread:
	clean_service_thread(gvt);
Z
Zhi Wang 已提交
297 298
out_clean_cmd_parser:
	intel_gvt_clean_cmd_parser(gvt);
299 300
out_clean_sched_policy:
	intel_gvt_clean_sched_policy(gvt);
Z
Zhi Wang 已提交
301 302
out_clean_workload_scheduler:
	intel_gvt_clean_workload_scheduler(gvt);
303 304
out_clean_opregion:
	intel_gvt_clean_opregion(gvt);
305 306
out_clean_gtt:
	intel_gvt_clean_gtt(gvt);
307 308
out_clean_irq:
	intel_gvt_clean_irq(gvt);
309 310
out_free_firmware:
	intel_gvt_free_firmware(gvt);
311 312
out_clean_mmio_info:
	intel_gvt_clean_mmio_info(gvt);
313 314
out_clean_idr:
	idr_destroy(&gvt->vgpu_idr);
315
	kfree(gvt);
316
	return ret;
317
}