0001
0002
0003
0004
0005
0006 #include "cmd.h"
0007
0008 static int mlx5vf_cmd_get_vhca_id(struct mlx5_core_dev *mdev, u16 function_id,
0009 u16 *vhca_id);
0010
0011 int mlx5vf_cmd_suspend_vhca(struct mlx5vf_pci_core_device *mvdev, u16 op_mod)
0012 {
0013 u32 out[MLX5_ST_SZ_DW(suspend_vhca_out)] = {};
0014 u32 in[MLX5_ST_SZ_DW(suspend_vhca_in)] = {};
0015
0016 lockdep_assert_held(&mvdev->state_mutex);
0017 if (mvdev->mdev_detach)
0018 return -ENOTCONN;
0019
0020 MLX5_SET(suspend_vhca_in, in, opcode, MLX5_CMD_OP_SUSPEND_VHCA);
0021 MLX5_SET(suspend_vhca_in, in, vhca_id, mvdev->vhca_id);
0022 MLX5_SET(suspend_vhca_in, in, op_mod, op_mod);
0023
0024 return mlx5_cmd_exec_inout(mvdev->mdev, suspend_vhca, in, out);
0025 }
0026
0027 int mlx5vf_cmd_resume_vhca(struct mlx5vf_pci_core_device *mvdev, u16 op_mod)
0028 {
0029 u32 out[MLX5_ST_SZ_DW(resume_vhca_out)] = {};
0030 u32 in[MLX5_ST_SZ_DW(resume_vhca_in)] = {};
0031
0032 lockdep_assert_held(&mvdev->state_mutex);
0033 if (mvdev->mdev_detach)
0034 return -ENOTCONN;
0035
0036 MLX5_SET(resume_vhca_in, in, opcode, MLX5_CMD_OP_RESUME_VHCA);
0037 MLX5_SET(resume_vhca_in, in, vhca_id, mvdev->vhca_id);
0038 MLX5_SET(resume_vhca_in, in, op_mod, op_mod);
0039
0040 return mlx5_cmd_exec_inout(mvdev->mdev, resume_vhca, in, out);
0041 }
0042
0043 int mlx5vf_cmd_query_vhca_migration_state(struct mlx5vf_pci_core_device *mvdev,
0044 size_t *state_size)
0045 {
0046 u32 out[MLX5_ST_SZ_DW(query_vhca_migration_state_out)] = {};
0047 u32 in[MLX5_ST_SZ_DW(query_vhca_migration_state_in)] = {};
0048 int ret;
0049
0050 lockdep_assert_held(&mvdev->state_mutex);
0051 if (mvdev->mdev_detach)
0052 return -ENOTCONN;
0053
0054 MLX5_SET(query_vhca_migration_state_in, in, opcode,
0055 MLX5_CMD_OP_QUERY_VHCA_MIGRATION_STATE);
0056 MLX5_SET(query_vhca_migration_state_in, in, vhca_id, mvdev->vhca_id);
0057 MLX5_SET(query_vhca_migration_state_in, in, op_mod, 0);
0058
0059 ret = mlx5_cmd_exec_inout(mvdev->mdev, query_vhca_migration_state, in,
0060 out);
0061 if (ret)
0062 return ret;
0063
0064 *state_size = MLX5_GET(query_vhca_migration_state_out, out,
0065 required_umem_size);
0066 return 0;
0067 }
0068
0069 static int mlx5fv_vf_event(struct notifier_block *nb,
0070 unsigned long event, void *data)
0071 {
0072 struct mlx5vf_pci_core_device *mvdev =
0073 container_of(nb, struct mlx5vf_pci_core_device, nb);
0074
0075 mutex_lock(&mvdev->state_mutex);
0076 switch (event) {
0077 case MLX5_PF_NOTIFY_ENABLE_VF:
0078 mvdev->mdev_detach = false;
0079 break;
0080 case MLX5_PF_NOTIFY_DISABLE_VF:
0081 mlx5vf_disable_fds(mvdev);
0082 mvdev->mdev_detach = true;
0083 break;
0084 default:
0085 break;
0086 }
0087 mlx5vf_state_mutex_unlock(mvdev);
0088 return 0;
0089 }
0090
0091 void mlx5vf_cmd_close_migratable(struct mlx5vf_pci_core_device *mvdev)
0092 {
0093 if (!mvdev->migrate_cap)
0094 return;
0095
0096 mutex_lock(&mvdev->state_mutex);
0097 mlx5vf_disable_fds(mvdev);
0098 mlx5vf_state_mutex_unlock(mvdev);
0099 }
0100
0101 void mlx5vf_cmd_remove_migratable(struct mlx5vf_pci_core_device *mvdev)
0102 {
0103 if (!mvdev->migrate_cap)
0104 return;
0105
0106 mlx5_sriov_blocking_notifier_unregister(mvdev->mdev, mvdev->vf_id,
0107 &mvdev->nb);
0108 destroy_workqueue(mvdev->cb_wq);
0109 }
0110
0111 void mlx5vf_cmd_set_migratable(struct mlx5vf_pci_core_device *mvdev,
0112 const struct vfio_migration_ops *mig_ops)
0113 {
0114 struct pci_dev *pdev = mvdev->core_device.pdev;
0115 int ret;
0116
0117 if (!pdev->is_virtfn)
0118 return;
0119
0120 mvdev->mdev = mlx5_vf_get_core_dev(pdev);
0121 if (!mvdev->mdev)
0122 return;
0123
0124 if (!MLX5_CAP_GEN(mvdev->mdev, migration))
0125 goto end;
0126
0127 mvdev->vf_id = pci_iov_vf_id(pdev);
0128 if (mvdev->vf_id < 0)
0129 goto end;
0130
0131 if (mlx5vf_cmd_get_vhca_id(mvdev->mdev, mvdev->vf_id + 1,
0132 &mvdev->vhca_id))
0133 goto end;
0134
0135 mvdev->cb_wq = alloc_ordered_workqueue("mlx5vf_wq", 0);
0136 if (!mvdev->cb_wq)
0137 goto end;
0138
0139 mutex_init(&mvdev->state_mutex);
0140 spin_lock_init(&mvdev->reset_lock);
0141 mvdev->nb.notifier_call = mlx5fv_vf_event;
0142 ret = mlx5_sriov_blocking_notifier_register(mvdev->mdev, mvdev->vf_id,
0143 &mvdev->nb);
0144 if (ret) {
0145 destroy_workqueue(mvdev->cb_wq);
0146 goto end;
0147 }
0148
0149 mvdev->migrate_cap = 1;
0150 mvdev->core_device.vdev.migration_flags =
0151 VFIO_MIGRATION_STOP_COPY |
0152 VFIO_MIGRATION_P2P;
0153 mvdev->core_device.vdev.mig_ops = mig_ops;
0154
0155 end:
0156 mlx5_vf_put_core_dev(mvdev->mdev);
0157 }
0158
0159 static int mlx5vf_cmd_get_vhca_id(struct mlx5_core_dev *mdev, u16 function_id,
0160 u16 *vhca_id)
0161 {
0162 u32 in[MLX5_ST_SZ_DW(query_hca_cap_in)] = {};
0163 int out_size;
0164 void *out;
0165 int ret;
0166
0167 out_size = MLX5_ST_SZ_BYTES(query_hca_cap_out);
0168 out = kzalloc(out_size, GFP_KERNEL);
0169 if (!out)
0170 return -ENOMEM;
0171
0172 MLX5_SET(query_hca_cap_in, in, opcode, MLX5_CMD_OP_QUERY_HCA_CAP);
0173 MLX5_SET(query_hca_cap_in, in, other_function, 1);
0174 MLX5_SET(query_hca_cap_in, in, function_id, function_id);
0175 MLX5_SET(query_hca_cap_in, in, op_mod,
0176 MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE << 1 |
0177 HCA_CAP_OPMOD_GET_CUR);
0178
0179 ret = mlx5_cmd_exec_inout(mdev, query_hca_cap, in, out);
0180 if (ret)
0181 goto err_exec;
0182
0183 *vhca_id = MLX5_GET(query_hca_cap_out, out,
0184 capability.cmd_hca_cap.vhca_id);
0185
0186 err_exec:
0187 kfree(out);
0188 return ret;
0189 }
0190
0191 static int _create_state_mkey(struct mlx5_core_dev *mdev, u32 pdn,
0192 struct mlx5_vf_migration_file *migf, u32 *mkey)
0193 {
0194 size_t npages = DIV_ROUND_UP(migf->total_length, PAGE_SIZE);
0195 struct sg_dma_page_iter dma_iter;
0196 int err = 0, inlen;
0197 __be64 *mtt;
0198 void *mkc;
0199 u32 *in;
0200
0201 inlen = MLX5_ST_SZ_BYTES(create_mkey_in) +
0202 sizeof(*mtt) * round_up(npages, 2);
0203
0204 in = kvzalloc(inlen, GFP_KERNEL);
0205 if (!in)
0206 return -ENOMEM;
0207
0208 MLX5_SET(create_mkey_in, in, translations_octword_actual_size,
0209 DIV_ROUND_UP(npages, 2));
0210 mtt = (__be64 *)MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
0211
0212 for_each_sgtable_dma_page(&migf->table.sgt, &dma_iter, 0)
0213 *mtt++ = cpu_to_be64(sg_page_iter_dma_address(&dma_iter));
0214
0215 mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
0216 MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_MTT);
0217 MLX5_SET(mkc, mkc, lr, 1);
0218 MLX5_SET(mkc, mkc, lw, 1);
0219 MLX5_SET(mkc, mkc, rr, 1);
0220 MLX5_SET(mkc, mkc, rw, 1);
0221 MLX5_SET(mkc, mkc, pd, pdn);
0222 MLX5_SET(mkc, mkc, bsf_octword_size, 0);
0223 MLX5_SET(mkc, mkc, qpn, 0xffffff);
0224 MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT);
0225 MLX5_SET(mkc, mkc, translations_octword_size, DIV_ROUND_UP(npages, 2));
0226 MLX5_SET64(mkc, mkc, len, migf->total_length);
0227 err = mlx5_core_create_mkey(mdev, mkey, in, inlen);
0228 kvfree(in);
0229 return err;
0230 }
0231
0232 void mlx5vf_mig_file_cleanup_cb(struct work_struct *_work)
0233 {
0234 struct mlx5vf_async_data *async_data = container_of(_work,
0235 struct mlx5vf_async_data, work);
0236 struct mlx5_vf_migration_file *migf = container_of(async_data,
0237 struct mlx5_vf_migration_file, async_data);
0238 struct mlx5_core_dev *mdev = migf->mvdev->mdev;
0239
0240 mutex_lock(&migf->lock);
0241 if (async_data->status) {
0242 migf->is_err = true;
0243 wake_up_interruptible(&migf->poll_wait);
0244 }
0245 mutex_unlock(&migf->lock);
0246
0247 mlx5_core_destroy_mkey(mdev, async_data->mkey);
0248 dma_unmap_sgtable(mdev->device, &migf->table.sgt, DMA_FROM_DEVICE, 0);
0249 mlx5_core_dealloc_pd(mdev, async_data->pdn);
0250 kvfree(async_data->out);
0251 fput(migf->filp);
0252 }
0253
0254 static void mlx5vf_save_callback(int status, struct mlx5_async_work *context)
0255 {
0256 struct mlx5vf_async_data *async_data = container_of(context,
0257 struct mlx5vf_async_data, cb_work);
0258 struct mlx5_vf_migration_file *migf = container_of(async_data,
0259 struct mlx5_vf_migration_file, async_data);
0260
0261 if (!status) {
0262 WRITE_ONCE(migf->total_length,
0263 MLX5_GET(save_vhca_state_out, async_data->out,
0264 actual_image_size));
0265 wake_up_interruptible(&migf->poll_wait);
0266 }
0267
0268
0269
0270
0271
0272 async_data->status = status;
0273 queue_work(migf->mvdev->cb_wq, &async_data->work);
0274 }
0275
0276 int mlx5vf_cmd_save_vhca_state(struct mlx5vf_pci_core_device *mvdev,
0277 struct mlx5_vf_migration_file *migf)
0278 {
0279 u32 out_size = MLX5_ST_SZ_BYTES(save_vhca_state_out);
0280 u32 in[MLX5_ST_SZ_DW(save_vhca_state_in)] = {};
0281 struct mlx5vf_async_data *async_data;
0282 struct mlx5_core_dev *mdev;
0283 u32 pdn, mkey;
0284 int err;
0285
0286 lockdep_assert_held(&mvdev->state_mutex);
0287 if (mvdev->mdev_detach)
0288 return -ENOTCONN;
0289
0290 mdev = mvdev->mdev;
0291 err = mlx5_core_alloc_pd(mdev, &pdn);
0292 if (err)
0293 return err;
0294
0295 err = dma_map_sgtable(mdev->device, &migf->table.sgt, DMA_FROM_DEVICE,
0296 0);
0297 if (err)
0298 goto err_dma_map;
0299
0300 err = _create_state_mkey(mdev, pdn, migf, &mkey);
0301 if (err)
0302 goto err_create_mkey;
0303
0304 MLX5_SET(save_vhca_state_in, in, opcode,
0305 MLX5_CMD_OP_SAVE_VHCA_STATE);
0306 MLX5_SET(save_vhca_state_in, in, op_mod, 0);
0307 MLX5_SET(save_vhca_state_in, in, vhca_id, mvdev->vhca_id);
0308 MLX5_SET(save_vhca_state_in, in, mkey, mkey);
0309 MLX5_SET(save_vhca_state_in, in, size, migf->total_length);
0310
0311 async_data = &migf->async_data;
0312 async_data->out = kvzalloc(out_size, GFP_KERNEL);
0313 if (!async_data->out) {
0314 err = -ENOMEM;
0315 goto err_out;
0316 }
0317
0318
0319 migf->total_length = 0;
0320 get_file(migf->filp);
0321 async_data->mkey = mkey;
0322 async_data->pdn = pdn;
0323 err = mlx5_cmd_exec_cb(&migf->async_ctx, in, sizeof(in),
0324 async_data->out,
0325 out_size, mlx5vf_save_callback,
0326 &async_data->cb_work);
0327 if (err)
0328 goto err_exec;
0329
0330 return 0;
0331
0332 err_exec:
0333 fput(migf->filp);
0334 kvfree(async_data->out);
0335 err_out:
0336 mlx5_core_destroy_mkey(mdev, mkey);
0337 err_create_mkey:
0338 dma_unmap_sgtable(mdev->device, &migf->table.sgt, DMA_FROM_DEVICE, 0);
0339 err_dma_map:
0340 mlx5_core_dealloc_pd(mdev, pdn);
0341 return err;
0342 }
0343
0344 int mlx5vf_cmd_load_vhca_state(struct mlx5vf_pci_core_device *mvdev,
0345 struct mlx5_vf_migration_file *migf)
0346 {
0347 struct mlx5_core_dev *mdev;
0348 u32 out[MLX5_ST_SZ_DW(save_vhca_state_out)] = {};
0349 u32 in[MLX5_ST_SZ_DW(save_vhca_state_in)] = {};
0350 u32 pdn, mkey;
0351 int err;
0352
0353 lockdep_assert_held(&mvdev->state_mutex);
0354 if (mvdev->mdev_detach)
0355 return -ENOTCONN;
0356
0357 mutex_lock(&migf->lock);
0358 if (!migf->total_length) {
0359 err = -EINVAL;
0360 goto end;
0361 }
0362
0363 mdev = mvdev->mdev;
0364 err = mlx5_core_alloc_pd(mdev, &pdn);
0365 if (err)
0366 goto end;
0367
0368 err = dma_map_sgtable(mdev->device, &migf->table.sgt, DMA_TO_DEVICE, 0);
0369 if (err)
0370 goto err_reg;
0371
0372 err = _create_state_mkey(mdev, pdn, migf, &mkey);
0373 if (err)
0374 goto err_mkey;
0375
0376 MLX5_SET(load_vhca_state_in, in, opcode,
0377 MLX5_CMD_OP_LOAD_VHCA_STATE);
0378 MLX5_SET(load_vhca_state_in, in, op_mod, 0);
0379 MLX5_SET(load_vhca_state_in, in, vhca_id, mvdev->vhca_id);
0380 MLX5_SET(load_vhca_state_in, in, mkey, mkey);
0381 MLX5_SET(load_vhca_state_in, in, size, migf->total_length);
0382
0383 err = mlx5_cmd_exec_inout(mdev, load_vhca_state, in, out);
0384
0385 mlx5_core_destroy_mkey(mdev, mkey);
0386 err_mkey:
0387 dma_unmap_sgtable(mdev->device, &migf->table.sgt, DMA_TO_DEVICE, 0);
0388 err_reg:
0389 mlx5_core_dealloc_pd(mdev, pdn);
0390 end:
0391 mutex_unlock(&migf->lock);
0392 return err;
0393 }