Received: by 2002:a05:7412:d8a:b0:e2:908c:2ebd with SMTP id b10csp2539668rdg; Mon, 16 Oct 2023 07:31:22 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGH9PsUavdlwEHIbe63t0FjKMUHBDwF+RxlQzsbm9uaZ8Vbv9aXP/Db1A41UDqHSW/Wo1L1 X-Received: by 2002:a17:90b:1c89:b0:27c:f48e:e245 with SMTP id oo9-20020a17090b1c8900b0027cf48ee245mr15599943pjb.24.1697466681914; Mon, 16 Oct 2023 07:31:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1697466681; cv=none; d=google.com; s=arc-20160816; b=b6TTtVJTwHdiuDrKM/bG9KJDQWOe3IkSIpa954S/o0n7rtX9PaP1qtu7HSW7rn+FA+ Ej5H/yAdOAMlodoNdubDJ9RlEUpSms6qRFCcE9/Tiw7L3yd45Mj2CKeGHGz8fuLys1Cu /0DiLOZwIPRVaQgHYCqtHiec6N0w28lY8NTMeCotLfYJTDtNmsCLvCBbC/pX0qLM7G9o mDxck+tkwGAkU9ESJpMEuolMVVl2vzYNsKADm4LcHLuNnFUlqEOYEAXUlnktfWasWprr qOmzlFQxyEgbJo1Hmb6buJYLS6BG1BtQgc05majxna8olxnkSHImh+baesnvPMRs+33D 3XGg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:in-reply-to:from :references:cc:to:content-language:subject:user-agent:mime-version :date:message-id:dkim-signature; bh=/9DeJrRzQVtFOe1mNsNPjFdgBTi3N3+iWS6VDgRIWYw=; fh=om0Bi4dLmuzknwUma4wAiG5ZCCZqlQJ6IR6+Y6XUIBg=; b=isr8p+7Jov/wxFs6tt3160Uykna/Lmf0AB6oI7+QjA7zIH31F7iJa1eIoVcdzypwOY vZrRkxZAchPh+hfB7qiXTAgsCS5bF/1jwjFsrNjLtpK1mu6K4GL0VMLFeewkpueRPrmD ZoYgVQ8m+huVHLvRbpyvbq+ikcHbDYItk5mVF9VI3Kn3inAZIjF+9C0f6woyFxAlZ/Kr b7DYGfr7V/u63nyMZtahOsd4t1hr5UdxtiiHhWDbRUt4Z8n98gZrARYuB5Ol1GMQ+yfA o9y+PnP1/KUBJIHQx/KBA7c2qNNU3tY7K52W1dGiDyflcKp5x/BNduakfNR7VHPNK0hQ oglQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=VSTFGBLb; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:3 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from lipwig.vger.email (lipwig.vger.email. [2620:137:e000::3:3]) by mx.google.com with ESMTPS id m9-20020a654389000000b00565e42df278si10119436pgp.759.2023.10.16.07.31.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Oct 2023 07:31:21 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:3 as permitted sender) client-ip=2620:137:e000::3:3; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=VSTFGBLb; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:3 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by lipwig.vger.email (Postfix) with ESMTP id 6A1768046BD7; Mon, 16 Oct 2023 07:31:18 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at lipwig.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233736AbjJPOaq (ORCPT + 99 others); Mon, 16 Oct 2023 10:30:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38322 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233580AbjJPOal (ORCPT ); Mon, 16 Oct 2023 10:30:41 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8962D9F for ; Mon, 16 Oct 2023 07:29:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1697466596; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/9DeJrRzQVtFOe1mNsNPjFdgBTi3N3+iWS6VDgRIWYw=; b=VSTFGBLbDcEDgYnSov0V3HncV4/i3VK5yqlpEgfcSwaCxETwvPhyOUjaKwhBPfNsfp6+XD 0w/JeuHSDgHpV0EuqXYbka6ppPQO/bHs8O6kue06CmqDaptxJ1us1n1Ddp/HypLkjPe2R5 OORFPbZT+CQkRHzE902r2XphHmQHBnY= Received: from mail-qt1-f200.google.com (mail-qt1-f200.google.com [209.85.160.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-574--VeekrJzMxeo0PMo-AVEzA-1; Mon, 16 Oct 2023 10:29:40 -0400 X-MC-Unique: -VeekrJzMxeo0PMo-AVEzA-1 Received: by mail-qt1-f200.google.com with SMTP id d75a77b69052e-41957273209so43253601cf.3 for ; Mon, 16 Oct 2023 07:29:40 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697466579; x=1698071379; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=/9DeJrRzQVtFOe1mNsNPjFdgBTi3N3+iWS6VDgRIWYw=; b=tc0BJVAALkWovaHX2rBu6ukLpRQD6LO3dahDO0pytkhgE7C2jw0up/8LTSchDmneGm vv2FEhw67xsy19IZLt/qhi4lXUEZNWNvQky3g3R47xPjiBmwjNigaJUBZmy97JLxmbjP PmSfbTlMK9b4a2Q6AfLOjclPNIjEDFgyOWN0JySYi/zTww3O/xJZ+h5KE29Hw9PutAvQ oPavELWDaVqsn6kLYiGqcaqmKvTv7D+y5qC1YgXykx4Otzhzza0OjbwjcRIhJbhUlhHP u/QWFANTYmyVbUVGnSGCtM+jfF7DApGefombmOFvn+JwtjgHEck5UVPYErmflm3Z5Y+N +8QA== X-Gm-Message-State: AOJu0YwDtfnc8NcGK84b8PfMNF0MhTos0v8nmCQ9u0uCC/XKdRiaYBJZ qOluUozxMQauY9ZLFrYR19v9KkjSw0wXZxQADIrf/rA985Ce62+r/xVg8abCbgSDbx0HpSGv9/9 tkyfZxu5FipmY9SqM+zbWobi5MPPRiR33 X-Received: by 2002:a05:622a:1708:b0:419:5c79:946b with SMTP id h8-20020a05622a170800b004195c79946bmr40512982qtk.45.1697466579040; Mon, 16 Oct 2023 07:29:39 -0700 (PDT) X-Received: by 2002:a05:622a:1708:b0:419:5c79:946b with SMTP id h8-20020a05622a170800b004195c79946bmr40512956qtk.45.1697466578620; Mon, 16 Oct 2023 07:29:38 -0700 (PDT) Received: from ?IPV6:2a01:e0a:280:24f0:9db0:474c:ff43:9f5c? ([2a01:e0a:280:24f0:9db0:474c:ff43:9f5c]) by smtp.gmail.com with ESMTPSA id x26-20020ac84d5a000000b0041520676966sm3075725qtv.47.2023.10.16.07.29.37 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 16 Oct 2023 07:29:38 -0700 (PDT) Message-ID: <39773e3f-1fd1-4c0a-ad81-95f5442d4851@redhat.com> Date: Mon, 16 Oct 2023 16:29:36 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH 2/2] vfio/mtty: Enable migration support Content-Language: en-US To: Alex Williamson Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org References: <20231013195653.1222141-1-alex.williamson@redhat.com> <20231013195653.1222141-3-alex.williamson@redhat.com> From: =?UTF-8?Q?C=C3=A9dric_Le_Goater?= In-Reply-To: <20231013195653.1222141-3-alex.williamson@redhat.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-0.9 required=5.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lipwig.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (lipwig.vger.email [0.0.0.0]); Mon, 16 Oct 2023 07:31:18 -0700 (PDT) On 10/13/23 21:56, Alex Williamson wrote: > The mtty driver exposes a PCI serial device to userspace and therefore > makes an easy target for a sample device supporting migration. The device > does not make use of DMA, therefore we can easily claim support for the > migration P2P states, as well as dirty logging. This implementation also > makes use of PRE_COPY support in order to provide migration stream > compatibility testing, which should generally be considered good practice. > > Signed-off-by: Alex Williamson > --- > > Much of this should look familiar to vfio-pci variant driver developers > ;) LGTM, though, I would isolate the part doing the get/set of the device state in their own routine to avoid extra noise in mtty_save_device_data() and mtty_resume_write(). The size of the state could also be dynamic and computed like done in .migration_get_data_size() handler. This is only a sample, so it might be worth the extra effort. That said, this driver could be a summary of the best practice to follow when implementing support for VFIO migration. Thanks, C. > > samples/vfio-mdev/mtty.c | 582 +++++++++++++++++++++++++++++++++++++++ > 1 file changed, 582 insertions(+) > > diff --git a/samples/vfio-mdev/mtty.c b/samples/vfio-mdev/mtty.c > index 0a2760818e46..572f51342fbd 100644 > --- a/samples/vfio-mdev/mtty.c > +++ b/samples/vfio-mdev/mtty.c > @@ -29,6 +29,8 @@ > #include > #include > #include > +#include > + > /* > * #defines > */ > @@ -124,6 +126,29 @@ struct serial_port { > u8 intr_trigger_level; /* interrupt trigger level */ > }; > > +struct mtty_data { > + u64 magic; > +#define MTTY_MAGIC 0x7e9d09898c3e2c4e /* Nothing clever, just random */ > + u32 major_ver; > +#define MTTY_MAJOR_VER 1 > + u32 minor_ver; > +#define MTTY_MINOR_VER 0 > + u32 nr_ports; > + u32 flags; > + struct serial_port ports[2]; > +}; > + > +struct mdev_state; > + > +struct mtty_migration_file { > + struct file *filp; > + struct mutex lock; > + struct mdev_state *mdev_state; > + struct mtty_data data; > + ssize_t filled_size; > + u8 disabled:1; > +}; > + > /* State of each mdev device */ > struct mdev_state { > struct vfio_device vdev; > @@ -140,6 +165,12 @@ struct mdev_state { > struct mutex rxtx_lock; > struct vfio_device_info dev_info; > int nr_ports; > + struct mutex state_mutex; > + enum vfio_device_mig_state state; > + struct mutex reset_mutex; > + struct mtty_migration_file *saving_migf; > + struct mtty_migration_file *resuming_migf; > + u8 deferred_reset:1; > }; > > static struct mtty_type { > @@ -716,6 +747,534 @@ static ssize_t mdev_access(struct mdev_state *mdev_state, u8 *buf, size_t count, > return ret; > } > > +static void mtty_disable_file(struct mtty_migration_file *migf) > +{ > + mutex_lock(&migf->lock); > + migf->disabled = true; > + migf->filled_size = 0; > + migf->filp->f_pos = 0; > + mutex_unlock(&migf->lock); > +} > + > +static void mtty_disable_files(struct mdev_state *mdev_state) > +{ > + if (mdev_state->saving_migf) { > + mtty_disable_file(mdev_state->saving_migf); > + fput(mdev_state->saving_migf->filp); > + mdev_state->saving_migf = NULL; > + } > + > + if (mdev_state->resuming_migf) { > + mtty_disable_file(mdev_state->resuming_migf); > + fput(mdev_state->resuming_migf->filp); > + mdev_state->resuming_migf = NULL; > + } > +} > + > +static void mtty_state_mutex_unlock(struct mdev_state *mdev_state) > +{ > +again: > + mutex_lock(&mdev_state->reset_mutex); > + if (mdev_state->deferred_reset) { > + mdev_state->deferred_reset = false; > + mutex_unlock(&mdev_state->reset_mutex); > + mdev_state->state = VFIO_DEVICE_STATE_RUNNING; > + mtty_disable_files(mdev_state); > + goto again; > + } > + mutex_unlock(&mdev_state->state_mutex); > + mutex_unlock(&mdev_state->reset_mutex); > +} > + > +static int mtty_release_migf(struct inode *inode, struct file *filp) > +{ > + struct mtty_migration_file *migf = filp->private_data; > + > + mtty_disable_file(migf); > + mutex_destroy(&migf->lock); > + kfree(migf); > + > + return 0; > +} > + > +static long mtty_precopy_ioctl(struct file *filp, unsigned int cmd, > + unsigned long arg) > +{ > + struct mtty_migration_file *migf = filp->private_data; > + struct mdev_state *mdev_state = migf->mdev_state; > + loff_t *pos = &filp->f_pos; > + struct vfio_precopy_info info = {}; > + unsigned long minsz; > + int ret; > + > + if (cmd != VFIO_MIG_GET_PRECOPY_INFO) > + return -ENOTTY; > + > + minsz = offsetofend(struct vfio_precopy_info, dirty_bytes); > + > + if (copy_from_user(&info, (void __user *)arg, minsz)) > + return -EFAULT; > + if (info.argsz < minsz) > + return -EINVAL; > + > + mutex_lock(&mdev_state->state_mutex); > + if (mdev_state->state != VFIO_DEVICE_STATE_PRE_COPY && > + mdev_state->state != VFIO_DEVICE_STATE_PRE_COPY_P2P) { > + ret = -EINVAL; > + goto unlock; > + } > + > + mutex_lock(&migf->lock); > + > + if (migf->disabled) { > + mutex_unlock(&migf->lock); > + ret = -ENODEV; > + goto unlock; > + } > + > + if (*pos > migf->filled_size) { > + mutex_unlock(&migf->lock); > + ret = -EINVAL; > + goto unlock; > + } > + > + info.dirty_bytes = 0; > + info.initial_bytes = migf->filled_size - *pos; > + mutex_unlock(&migf->lock); > + > + ret = copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; > +unlock: > + mtty_state_mutex_unlock(mdev_state); > + return ret; > +} > + > +static ssize_t mtty_save_read(struct file *filp, char __user *buf, > + size_t len, loff_t *pos) > +{ > + struct mtty_migration_file *migf = filp->private_data; > + ssize_t ret = 0; > + > + if (pos) > + return -ESPIPE; > + > + pos = &filp->f_pos; > + > + mutex_lock(&migf->lock); > + > + dev_dbg(migf->mdev_state->vdev.dev, "%s ask %zu\n", __func__, len); > + > + if (migf->disabled) { > + ret = -ENODEV; > + goto out_unlock; > + } > + > + if (*pos > migf->filled_size) { > + ret = -EINVAL; > + goto out_unlock; > + } > + > + len = min_t(size_t, migf->filled_size - *pos, len); > + if (len) { > + if (copy_to_user(buf, (void *)&migf->data + *pos, len)) { > + ret = -EFAULT; > + goto out_unlock; > + } > + *pos += len; > + ret = len; > + } > +out_unlock: > + dev_dbg(migf->mdev_state->vdev.dev, "%s read %zu\n", __func__, ret); > + mutex_unlock(&migf->lock); > + return ret; > +} > + > +static const struct file_operations mtty_save_fops = { > + .owner = THIS_MODULE, > + .read = mtty_save_read, > + .unlocked_ioctl = mtty_precopy_ioctl, > + .compat_ioctl = compat_ptr_ioctl, > + .release = mtty_release_migf, > + .llseek = no_llseek, > +}; > + > +static struct mtty_migration_file * > +mtty_save_device_data(struct mdev_state *mdev_state, > + enum vfio_device_mig_state state) > +{ > + struct mtty_migration_file *migf = mdev_state->saving_migf; > + struct mtty_migration_file *ret = NULL; > + > + if (migf) { > + if (state == VFIO_DEVICE_STATE_STOP_COPY) > + goto fill_data; > + return ret; > + } > + > + migf = kzalloc(sizeof(*migf), GFP_KERNEL_ACCOUNT); > + if (!migf) > + return ERR_PTR(-ENOMEM); > + > + migf->filp = anon_inode_getfile("mtty_mig", &mtty_save_fops, > + migf, O_RDONLY); > + if (IS_ERR(migf->filp)) { > + int rc = PTR_ERR(migf->filp); > + > + kfree(migf); > + return ERR_PTR(rc); > + } > + > + stream_open(migf->filp->f_inode, migf->filp); > + mutex_init(&migf->lock); > + migf->mdev_state = mdev_state; > + > + migf->data.magic = MTTY_MAGIC; > + migf->data.major_ver = MTTY_MAJOR_VER; > + migf->data.minor_ver = MTTY_MINOR_VER; > + migf->data.nr_ports = mdev_state->nr_ports; > + > + migf->filled_size = offsetof(struct mtty_data, ports); > + > + dev_dbg(mdev_state->vdev.dev, "%s filled header to %zu\n", > + __func__, migf->filled_size); > + > + ret = mdev_state->saving_migf = migf; > + > +fill_data: > + if (state == VFIO_DEVICE_STATE_STOP_COPY) { > + int i; > + > + mutex_lock(&migf->lock); > + for (i = 0; i < mdev_state->nr_ports; i++) { > + memcpy(&migf->data.ports[i], > + &mdev_state->s[i], sizeof(struct serial_port)); > + migf->filled_size += sizeof(struct serial_port); > + } > + dev_dbg(mdev_state->vdev.dev, "%s filled to %zu\n", > + __func__, migf->filled_size); > + mutex_unlock(&migf->lock); > + } > + > + return ret; > +} > + > +static ssize_t mtty_resume_write(struct file *filp, const char __user *buf, > + size_t len, loff_t *pos) > +{ > + struct mtty_migration_file *migf = filp->private_data; > + loff_t requested_length; > + ssize_t ret = 0; > + > + if (pos) > + return -ESPIPE; > + > + pos = &filp->f_pos; > + > + if (*pos < 0 || > + check_add_overflow((loff_t)len, *pos, &requested_length)) > + return -EINVAL; > + > + if (requested_length > sizeof(struct mtty_data)) > + return -ENOMEM; > + > + mutex_lock(&migf->lock); > + > + if (migf->disabled) { > + ret = -ENODEV; > + goto out_unlock; > + } > + > + if (copy_from_user((void *)&migf->data + *pos, buf, len)) { > + ret = -EFAULT; > + goto out_unlock; > + } > + > + *pos += len; > + ret = len; > + migf->filled_size += len; > + > + dev_dbg(migf->mdev_state->vdev.dev, "%s received %zu, total %zu\n", > + __func__, len, migf->filled_size); > + > + if (migf->filled_size >= offsetof(struct mtty_data, ports)) { > + struct mdev_state *mdev_state = migf->mdev_state; > + > + if (migf->data.magic != MTTY_MAGIC || migf->data.flags || > + migf->data.major_ver != MTTY_MAJOR_VER || > + migf->data.minor_ver != MTTY_MINOR_VER || > + migf->data.nr_ports != mdev_state->nr_ports) { > + dev_dbg(migf->mdev_state->vdev.dev, > + "%s failed validation\n", __func__); > + ret = -EFAULT; > + } else { > + dev_dbg(migf->mdev_state->vdev.dev, > + "%s header validated\n", __func__); > + } > + } > + > +out_unlock: > + mutex_unlock(&migf->lock); > + return ret; > +} > + > +static const struct file_operations mtty_resume_fops = { > + .owner = THIS_MODULE, > + .write = mtty_resume_write, > + .release = mtty_release_migf, > + .llseek = no_llseek, > +}; > + > +static struct mtty_migration_file * > +mtty_resume_device_data(struct mdev_state *mdev_state) > +{ > + struct mtty_migration_file *migf; > + int ret; > + > + migf = kzalloc(sizeof(*migf), GFP_KERNEL_ACCOUNT); > + if (!migf) > + return ERR_PTR(-ENOMEM); > + > + migf->filp = anon_inode_getfile("mtty_mig", &mtty_resume_fops, > + migf, O_WRONLY); > + if (IS_ERR(migf->filp)) { > + ret = PTR_ERR(migf->filp); > + kfree(migf); > + return ERR_PTR(ret); > + } > + > + stream_open(migf->filp->f_inode, migf->filp); > + mutex_init(&migf->lock); > + migf->mdev_state = mdev_state; > + > + mdev_state->resuming_migf = migf; > + > + return migf; > +} > + > +static int mtty_load_state(struct mdev_state *mdev_state) > +{ > + struct mtty_migration_file *migf = mdev_state->resuming_migf; > + int i; > + > + mutex_lock(&migf->lock); > + /* magic and version already tested by resume write fn */ > + if (migf->filled_size < offsetof(struct mtty_data, ports) + > + (mdev_state->nr_ports * sizeof(struct serial_port))) { > + dev_dbg(mdev_state->vdev.dev, "%s expected %zu, got %zu\n", > + __func__, offsetof(struct mtty_data, ports) + > + (mdev_state->nr_ports * sizeof(struct serial_port)), > + migf->filled_size); > + mutex_unlock(&migf->lock); > + return -EINVAL; > + } > + > + for (i = 0; i < mdev_state->nr_ports; i++) > + memcpy(&mdev_state->s[i], > + &migf->data.ports[i], sizeof(struct serial_port)); > + > + mutex_unlock(&migf->lock); > + return 0; > +} > + > +static struct file *mtty_step_state(struct mdev_state *mdev_state, > + enum vfio_device_mig_state new) > +{ > + enum vfio_device_mig_state cur = mdev_state->state; > + > + dev_dbg(mdev_state->vdev.dev, "%s: %d -> %d\n", __func__, cur, new); > + > + /* > + * The following state transitions are no-op considering > + * mtty does not do DMA nor require any explicit start/stop. > + * > + * RUNNING -> RUNNING_P2P > + * RUNNING_P2P -> RUNNING > + * RUNNING_P2P -> STOP > + * PRE_COPY -> PRE_COPY_P2P > + * PRE_COPY_P2P -> PRE_COPY > + * STOP -> RUNNING_P2P > + */ > + if ((cur == VFIO_DEVICE_STATE_RUNNING && > + new == VFIO_DEVICE_STATE_RUNNING_P2P) || > + (cur == VFIO_DEVICE_STATE_RUNNING_P2P && > + (new == VFIO_DEVICE_STATE_RUNNING || > + new == VFIO_DEVICE_STATE_STOP)) || > + (cur == VFIO_DEVICE_STATE_PRE_COPY && > + new == VFIO_DEVICE_STATE_PRE_COPY_P2P) || > + (cur == VFIO_DEVICE_STATE_PRE_COPY_P2P && > + new == VFIO_DEVICE_STATE_PRE_COPY) || > + (cur == VFIO_DEVICE_STATE_STOP && > + new == VFIO_DEVICE_STATE_RUNNING_P2P)) > + return NULL; > + > + /* > + * The following state transitions simply close migration files, > + * with the exception of RESUMING -> STOP, which needs to load > + * the state first. > + * > + * RESUMING -> STOP > + * PRE_COPY -> RUNNING > + * PRE_COPY_P2P -> RUNNING_P2P > + * STOP_COPY -> STOP > + */ > + if (cur == VFIO_DEVICE_STATE_RESUMING && > + new == VFIO_DEVICE_STATE_STOP) { > + int ret; > + > + ret = mtty_load_state(mdev_state); > + if (ret) > + return ERR_PTR(ret); > + mtty_disable_files(mdev_state); > + return NULL; > + } > + > + if ((cur == VFIO_DEVICE_STATE_PRE_COPY && > + new == VFIO_DEVICE_STATE_RUNNING) || > + (cur == VFIO_DEVICE_STATE_PRE_COPY_P2P && > + new == VFIO_DEVICE_STATE_RUNNING_P2P) || > + (cur == VFIO_DEVICE_STATE_STOP_COPY && > + new == VFIO_DEVICE_STATE_STOP)) { > + mtty_disable_files(mdev_state); > + return NULL; > + } > + > + /* > + * The following state transitions return migration files. > + * > + * RUNNING -> PRE_COPY > + * RUNNING_P2P -> PRE_COPY_P2P > + * STOP -> STOP_COPY > + * STOP -> RESUMING > + * PRE_COPY_P2P -> STOP_COPY > + */ > + if ((cur == VFIO_DEVICE_STATE_RUNNING && > + new == VFIO_DEVICE_STATE_PRE_COPY) || > + (cur == VFIO_DEVICE_STATE_RUNNING_P2P && > + new == VFIO_DEVICE_STATE_PRE_COPY_P2P) || > + (cur == VFIO_DEVICE_STATE_STOP && > + new == VFIO_DEVICE_STATE_STOP_COPY) || > + (cur == VFIO_DEVICE_STATE_PRE_COPY_P2P && > + new == VFIO_DEVICE_STATE_STOP_COPY)) { > + struct mtty_migration_file *migf; > + > + migf = mtty_save_device_data(mdev_state, new); > + if (IS_ERR(migf)) > + return ERR_CAST(migf); > + > + if (migf) { > + get_file(migf->filp); > + > + return migf->filp; > + } > + return NULL; > + } > + > + if (cur == VFIO_DEVICE_STATE_STOP && > + new == VFIO_DEVICE_STATE_RESUMING) { > + struct mtty_migration_file *migf; > + > + migf = mtty_resume_device_data(mdev_state); > + if (IS_ERR(migf)) > + return ERR_CAST(migf); > + > + get_file(migf->filp); > + > + return migf->filp; > + } > + > + /* vfio_mig_get_next_state() does not use arcs other than the above */ > + WARN_ON(true); > + return ERR_PTR(-EINVAL); > +} > + > +static struct file *mtty_set_state(struct vfio_device *vdev, > + enum vfio_device_mig_state new_state) > +{ > + struct mdev_state *mdev_state = > + container_of(vdev, struct mdev_state, vdev); > + struct file *ret = NULL; > + > + dev_dbg(vdev->dev, "%s -> %d\n", __func__, new_state); > + > + mutex_lock(&mdev_state->state_mutex); > + while (mdev_state->state != new_state) { > + enum vfio_device_mig_state next_state; > + int rc = vfio_mig_get_next_state(vdev, mdev_state->state, > + new_state, &next_state); > + if (rc) { > + ret = ERR_PTR(rc); > + break; > + } > + > + ret = mtty_step_state(mdev_state, next_state); > + if (IS_ERR(ret)) > + break; > + > + mdev_state->state = next_state; > + > + if (WARN_ON(ret && new_state != next_state)) { > + fput(ret); > + ret = ERR_PTR(-EINVAL); > + break; > + } > + } > + mtty_state_mutex_unlock(mdev_state); > + return ret; > +} > + > +static int mtty_get_state(struct vfio_device *vdev, > + enum vfio_device_mig_state *current_state) > +{ > + struct mdev_state *mdev_state = > + container_of(vdev, struct mdev_state, vdev); > + > + mutex_lock(&mdev_state->state_mutex); > + *current_state = mdev_state->state; > + mtty_state_mutex_unlock(mdev_state); > + return 0; > +} > + > +static int mtty_get_data_size(struct vfio_device *vdev, > + unsigned long *stop_copy_length) > +{ > + struct mdev_state *mdev_state = > + container_of(vdev, struct mdev_state, vdev); > + > + *stop_copy_length = offsetof(struct mtty_data, ports) + > + (mdev_state->nr_ports * sizeof(struct serial_port)); > + return 0; > +} > + > +static const struct vfio_migration_ops mtty_migration_ops = { > + .migration_set_state = mtty_set_state, > + .migration_get_state = mtty_get_state, > + .migration_get_data_size = mtty_get_data_size, > +}; > + > +static int mtty_log_start(struct vfio_device *vdev, > + struct rb_root_cached *ranges, > + u32 nnodes, u64 *page_size) > +{ > + return 0; > +} > + > +static int mtty_log_stop(struct vfio_device *vdev) > +{ > + return 0; > +} > + > +static int mtty_log_read_and_clear(struct vfio_device *vdev, > + unsigned long iova, unsigned long length, > + struct iova_bitmap *dirty) > +{ > + return 0; > +} > + > +static const struct vfio_log_ops mtty_log_ops = { > + .log_start = mtty_log_start, > + .log_stop = mtty_log_stop, > + .log_read_and_clear = mtty_log_read_and_clear, > +}; > + > static int mtty_init_dev(struct vfio_device *vdev) > { > struct mdev_state *mdev_state = > @@ -748,6 +1307,16 @@ static int mtty_init_dev(struct vfio_device *vdev) > mutex_init(&mdev_state->ops_lock); > mdev_state->mdev = mdev; > mtty_create_config_space(mdev_state); > + > + mutex_init(&mdev_state->state_mutex); > + mutex_init(&mdev_state->reset_mutex); > + vdev->migration_flags = VFIO_MIGRATION_STOP_COPY | > + VFIO_MIGRATION_P2P | > + VFIO_MIGRATION_PRE_COPY; > + vdev->mig_ops = &mtty_migration_ops; > + vdev->log_ops = &mtty_log_ops; > + mdev_state->state = VFIO_DEVICE_STATE_RUNNING; > + > return 0; > > err_nr_ports: > @@ -781,6 +1350,8 @@ static void mtty_release_dev(struct vfio_device *vdev) > struct mdev_state *mdev_state = > container_of(vdev, struct mdev_state, vdev); > > + mutex_destroy(&mdev_state->reset_mutex); > + mutex_destroy(&mdev_state->state_mutex); > atomic_add(mdev_state->nr_ports, &mdev_avail_ports); > kfree(mdev_state->vconfig); > } > @@ -797,6 +1368,15 @@ static int mtty_reset(struct mdev_state *mdev_state) > { > pr_info("%s: called\n", __func__); > > + mutex_lock(&mdev_state->reset_mutex); > + mdev_state->deferred_reset = true; > + if (!mutex_trylock(&mdev_state->state_mutex)) { > + mutex_unlock(&mdev_state->reset_mutex); > + return 0; > + } > + mutex_unlock(&mdev_state->reset_mutex); > + mtty_state_mutex_unlock(mdev_state); > + > return 0; > } > > @@ -1268,6 +1848,8 @@ static void mtty_close(struct vfio_device *vdev) > struct mdev_state *mdev_state = > container_of(vdev, struct mdev_state, vdev); > > + mtty_disable_files(mdev_state); > + > if (mdev_state->intx_evtfd) { > eventfd_ctx_put(mdev_state->intx_evtfd); > mdev_state->intx_evtfd = NULL;