diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index c4aeeefb3305..f7a2d6bd1c14 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -73,7 +73,7 @@ MODULE_VERSION(NTB_TRANSPORT_VER); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel Corporation"); -static unsigned long max_mw_size; +static unsigned long max_mw_size = 256 * 1024 * 1024; module_param(max_mw_size, ulong, 0644); MODULE_PARM_DESC(max_mw_size, "Limit size of large memory windows"); @@ -927,8 +927,8 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, xlat_size = round_up(size, xlat_align_size); buff_size = round_up(size, xlat_align); - /* No need to re-setup */ - if (mw->xlat_size == xlat_size) + /* No need to re-setup if size already matches */ + if (mw->xlat_size == xlat_size && mw->buff_size == buff_size) return 0; if (mw->buff_size) @@ -1048,8 +1048,11 @@ static void ntb_transport_link_cleanup(struct ntb_transport_ctx *nt) if (!nt->link_is_up) cancel_delayed_work_sync(&nt->link_work); - for (i = 0; i < nt->mw_count; i++) - ntb_free_mw(nt, i); + /* + * Do NOT free MW memory on link down. Memory is retained across + * link cycles to avoid fragmentation from repeated allocation. + * Memory is only freed on device removal in ntb_transport_free(). + */ /* The scratchpad registers keep the values if the remote side * goes down, blast them now to give them a sane value the next @@ -1332,6 +1335,34 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt, return 0; } +/* + * Speculatively pre-allocate memory assuming symmetric config. + * This grabs contiguous memory early before fragmentation. + * If peer has different size, we'll reallocate on link-up. + */ +static void ntb_preallocate_mws(struct ntb_transport_ctx *nt) +{ + struct ntb_transport_mw *mw; + resource_size_t size; + int i, rc; + + for (i = 0; i < nt->mw_count; i++) { + mw = &nt->mw_vec[i]; + size = mw->phys_size; + + if (max_mw_size && size > max_mw_size) + size = max_mw_size; + + rc = ntb_set_mw(nt, i, size); + if (rc) { + dev_info(&nt->ndev->pdev->dev, + "Failed to preallocate MW%d (size %llx): %d\n", + i, (unsigned long long)size, rc); + /* Continue - link-up will retry */ + } + } +} + static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev) { struct ntb_transport_ctx *nt; @@ -1476,6 +1507,9 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev) INIT_WORK(&nt->link_cleanup, ntb_transport_link_cleanup_work); nt->link_is_up = false; + /* Speculatively pre-allocate MW buffers to avoid fragmentation */ + ntb_preallocate_mws(nt); + rc = ntb_set_ctx(ndev, nt, &ntb_transport_ops); if (rc) goto err2; @@ -1495,9 +1529,11 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev) err2: kfree(nt->qp_vec); err1: - while (i--) { + for (i = 0; i < mw_count; i++) { mw = &nt->mw_vec[i]; - iounmap(mw->vbase); + ntb_free_mw(nt, i); + if (mw->vbase) + iounmap(mw->vbase); } kfree(nt->mw_vec); err: diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 4e52820810fa..371103667ccd 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -188,6 +188,20 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, nfsd_mountpoint(dentry, exp) == 2) { /* This is only a mountpoint in some other namespace */ path_put(&path); +#ifdef CONFIG_TRUENAS + /* + * For snapdir entries we set LOOKUP_AUTOMOUNT above, so + * if the path is unchanged the automount was attempted + * and failed (EISDIR from zfsctl_snapshot_mount). This + * can happen transiently when zfs_suspend_fs races with + * the mount helper after the z_teardown_lock deadlock + * fix (see https://github.com/openzfs/zfs/pull/18415). + * Return ESTALE so the client retries via LOOKUP rather + * than caching the ctldir stub as an empty directory. + */ + if (is_snapdir) + err = -ESTALE; +#endif /* CONFIG_TRUENAS */ goto out; } @@ -201,8 +215,23 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, * allowed without an explicit export of the new * directory. */ - if (err == -ENOENT && !(exp->ex_flags & NFSEXP_V4ROOT)) - err = 0; + if (err == -ENOENT && !(exp->ex_flags & NFSEXP_V4ROOT)) { +#ifdef CONFIG_TRUENAS + /* + * For ZFS snapshot entries under a zfs_snapdir + * export, the fallback dentry is an automount + * stub with simple_dir_operations that returns + * empty READDIR (NFS4_OK, zero entries). The + * client caches this silently with no error + * signal to trigger re-resolution. Return ESTALE + * so the client retries via LOOKUP. + */ + if (is_snapdir) + err = -ESTALE; + else +#endif /* CONFIG_TRUENAS */ + err = 0; + } path_put(&path); goto out; }