Received: by 2002:a05:7412:d8a:b0:e2:908c:2ebd with SMTP id b10csp3208529rdg; Tue, 17 Oct 2023 07:46:08 -0700 (PDT) X-Google-Smtp-Source: AGHT+IH3M9SCrT/8ZDNjYuheyfv07nUK71HXwNjhypRyInx0D/Qlff81aCTAGbbCurcnTBaojwfu X-Received: by 2002:a05:6a00:9a9:b0:6be:5367:211d with SMTP id u41-20020a056a0009a900b006be5367211dmr3023966pfg.24.1697553968122; Tue, 17 Oct 2023 07:46:08 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1697553968; cv=none; d=google.com; s=arc-20160816; b=IC15qsfTjwclpGXUlbwInaX/qZ84EIrNDfdqNJTdqs5Yt7F6rfWI7/X1f+KNUs79oB WkUW2rlTTy8cWlTD5ImHKQAY5tEsD5o4XUZzBLiUoFVGHbS6lfUAxlU0Z5s1IdLokxky zHULoqf7EBivm3bYcEA7XUNUrOIS65rorhtC4i4cd3DAjqzNrt3b90Kw0zLgHufjhrcE OtYR3EoMQYiVrSF78V5deuha3Tiv4YTBpqiNOMxrgXnEny22+I307Yc+qgB+wbquae0n g1oX10HCowNinv3NqpDDVcfkpSVv5HZEetGvh/1qP4bpS1RaK3ac5rEM0CosEDw7tmLr OwpA== 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=vWgnD0LoPLLR3wBGXxE/FYKfIng4FodU93H6sSGXmg0=; fh=om0Bi4dLmuzknwUma4wAiG5ZCCZqlQJ6IR6+Y6XUIBg=; b=MqqwGg5bagYfCKnP8e/6GSHOVgBfq84pUPOczBHvyuZpWodpwgCJ/oVz4UIkTYQZBy l6Q75sIvyZs8m84X54XMiKsy0ZyR7ap28aRW81hCeSfjvMqZnNul9YJ/jGuh9k9Zb+a3 K1f0B2LENpkLeOYoDPGxppJAy3SCHARFUnqhpK8ZqW95LEFCVcVAtDis77a6Yw0bA44o 3YapMGa/zX3PFuE1UVppZ7hbLH87MKGIFkmtz7ZUx9kyAjK0tRghg/gdZAqReY0lAjIi drDhr3Iy9lntJ/vPiCuXzUMfujVvEVHtrMMKxPfnt7+vYiR08jcE7LBo1930Ss7Fd0Jw lNGw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=Tp54+1Rl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.31 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 morse.vger.email (morse.vger.email. [23.128.96.31]) by mx.google.com with ESMTPS id a191-20020a6390c8000000b005b106cd44d2si1975941pge.620.2023.10.17.07.46.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 17 Oct 2023 07:46:08 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.31 as permitted sender) client-ip=23.128.96.31; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=Tp54+1Rl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.31 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 morse.vger.email (Postfix) with ESMTP id 234F480DA8DE; Tue, 17 Oct 2023 07:45:54 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at morse.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344091AbjJQOpf (ORCPT + 99 others); Tue, 17 Oct 2023 10:45:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56254 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235026AbjJQOpc (ORCPT ); Tue, 17 Oct 2023 10:45:32 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EC408F0 for ; Tue, 17 Oct 2023 07:44:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1697553883; 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=vWgnD0LoPLLR3wBGXxE/FYKfIng4FodU93H6sSGXmg0=; b=Tp54+1RlX3aCZs+5CYXNs2sz6lbNgkQ6sWR+U702nuHZKUEQfuC3SFW3e5+zCHKbfl6+q/ gyMxurqGyVZEW4dq76TyHpkyUniZP9nXCjnYPgd/W9qxT5mrT6XLUYT7YhjA2fkpAbZgNW FafKmTH6/m3lO/OhmCzf0b0A38Sdhu8= Received: from mail-yb1-f197.google.com (mail-yb1-f197.google.com [209.85.219.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-179-ZlmejDfgO9m5x21oUPZyMg-1; Tue, 17 Oct 2023 10:44:41 -0400 X-MC-Unique: ZlmejDfgO9m5x21oUPZyMg-1 Received: by mail-yb1-f197.google.com with SMTP id 3f1490d57ef6-d9a3add086dso7083574276.3 for ; Tue, 17 Oct 2023 07:44:41 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697553881; x=1698158681; 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=vWgnD0LoPLLR3wBGXxE/FYKfIng4FodU93H6sSGXmg0=; b=v5LiEv7DpXWxWzI7ed04/A1pdbxVO7RkTpG+Fxv8ovWSGemMi+XapUJkkuLOwGLeEx B2UtGjyyqqL9pQ+uK7oqq9jHUODHrM7NaQsMdpya/dgbUh3Okzh3aS4ab7UkzqaR1HIO QDFm6Xp9HPODqRkve2EAQ54XqXwYa3+1uQfSvX9OCnGz6Z4axre0pzvbWgqSRwMEIbi2 /oNcnYxqOTSF0dr5GBAOxNnG0g2Elvdlouvb1ehnkCVjbcj5pjP52aDL/IW2xOTThfeI l5T3JaNY8Y8NeVl+B9816zWeJN9wmuQoU55pkr5uvBJHhfro1VVuprIy/XwqwL1ffoK6 2tBA== X-Gm-Message-State: AOJu0YzveQ89S045Nwata5+GmkyNpNC70G3yPCJrFmBAHKPP0JKeN5cm 9Je50WKWEIN831kE/xrEbS+q4sfk5Pg+356JAmVs8EOI6O4+RYBOlcaRnF1obNCzTfwxgPSkgXv sI0JkuU4h1JoJUpLZehw3GKgY X-Received: by 2002:a5b:44:0:b0:d89:469a:536d with SMTP id e4-20020a5b0044000000b00d89469a536dmr2267385ybp.47.1697553881118; Tue, 17 Oct 2023 07:44:41 -0700 (PDT) X-Received: by 2002:a5b:44:0:b0:d89:469a:536d with SMTP id e4-20020a5b0044000000b00d89469a536dmr2267363ybp.47.1697553880738; Tue, 17 Oct 2023 07:44:40 -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 es16-20020a056214193000b0064f53943626sm602068qvb.89.2023.10.17.07.44.39 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 17 Oct 2023 07:44:40 -0700 (PDT) Message-ID: <24a237f2-b9ac-41d8-9488-0056da34425e@redhat.com> Date: Tue, 17 Oct 2023 16:44:38 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v2 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: <20231016224736.2575718-1-alex.williamson@redhat.com> <20231016224736.2575718-3-alex.williamson@redhat.com> From: =?UTF-8?Q?C=C3=A9dric_Le_Goater?= In-Reply-To: <20231016224736.2575718-3-alex.williamson@redhat.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit 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 morse.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 (morse.vger.email [0.0.0.0]); Tue, 17 Oct 2023 07:45:54 -0700 (PDT) On 10/17/23 00:47, 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 Reviewed-by: Cédric Le Goater Thanks, C. > --- > samples/vfio-mdev/mtty.c | 590 +++++++++++++++++++++++++++++++++++++++ > 1 file changed, 590 insertions(+) > > diff --git a/samples/vfio-mdev/mtty.c b/samples/vfio-mdev/mtty.c > index 245db52bedf2..69ba0281f9e0 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; > + enum vfio_device_mig_state state; > + struct mutex state_mutex; > + struct mutex reset_mutex; > + struct mtty_migration_file *saving_migf; > + struct mtty_migration_file *resuming_migf; > + u8 deferred_reset:1; > u8 intx_mask:1; > }; > > @@ -743,6 +774,543 @@ static ssize_t mdev_access(struct mdev_state *mdev_state, u8 *buf, size_t count, > return ret; > } > > +static size_t mtty_data_size(struct mdev_state *mdev_state) > +{ > + return offsetof(struct mtty_data, ports) + > + (mdev_state->nr_ports * sizeof(struct serial_port)); > +} > + > +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 void mtty_save_state(struct mdev_state *mdev_state) > +{ > + struct mtty_migration_file *migf = mdev_state->saving_migf; > + 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); > +} > + > +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 < mtty_data_size(mdev_state)) { > + dev_dbg(mdev_state->vdev.dev, "%s expected %zu, got %zu\n", > + __func__, mtty_data_size(mdev_state), > + 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 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) > + mtty_save_state(mdev_state); > + > + 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; > + struct mdev_state *mdev_state = migf->mdev_state; > + 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 > mtty_data_size(mdev_state)) > + 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; > + > + dev_dbg(migf->mdev_state->vdev.dev, "%s received %zu, total %zu\n", > + __func__, len, migf->filled_size + len); > + > + if (migf->filled_size < offsetof(struct mtty_data, ports) && > + migf->filled_size + len >= offsetof(struct mtty_data, ports)) { > + 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__); > + } > + } > + > + migf->filled_size += len; > + > +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 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 = mtty_data_size(mdev_state); > + 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 = > @@ -775,6 +1343,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: > @@ -808,6 +1386,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); > } > @@ -824,6 +1404,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; > } > > @@ -1350,6 +1939,7 @@ 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); > mtty_disable_intx(mdev_state); > mtty_disable_msi(mdev_state); > }