Message ID | 20220407154717.7695-21-logang@deltatee.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Userspace P2PDMA with O_DIRECT NVMe devices | expand |
On Thu, Apr 07, 2022 at 09:47:16AM -0600, Logan Gunthorpe wrote: > +static void pci_p2pdma_unmap_mappings(void *data) > +{ > + struct pci_dev *pdev = data; > + struct pci_p2pdma *p2pdma = rcu_dereference_protected(pdev->p2pdma, 1); > + > + /* Ensure no new pages can be allocated in mappings */ > + p2pdma->active = false; > + synchronize_rcu(); > + > + unmap_mapping_range(p2pdma->inode->i_mapping, 0, 0, 1); > + > + /* > + * On some architectures, TLB flushes are done with call_rcu() > + * so to ensure GUP fast is done with the pages, call synchronize_rcu() > + * before freeing them. > + */ > + synchronize_rcu(); > + pci_p2pdma_free_mappings(p2pdma->inode->i_mapping); With the series from Felix getting close this should get updated to not set pte_devmap and use proper natural refcounting without any of this stuff. Jason
On 2022-05-27 06:55, Jason Gunthorpe wrote: > On Thu, Apr 07, 2022 at 09:47:16AM -0600, Logan Gunthorpe wrote: >> +static void pci_p2pdma_unmap_mappings(void *data) >> +{ >> + struct pci_dev *pdev = data; >> + struct pci_p2pdma *p2pdma = rcu_dereference_protected(pdev->p2pdma, 1); >> + >> + /* Ensure no new pages can be allocated in mappings */ >> + p2pdma->active = false; >> + synchronize_rcu(); >> + >> + unmap_mapping_range(p2pdma->inode->i_mapping, 0, 0, 1); >> + >> + /* >> + * On some architectures, TLB flushes are done with call_rcu() >> + * so to ensure GUP fast is done with the pages, call synchronize_rcu() >> + * before freeing them. >> + */ >> + synchronize_rcu(); >> + pci_p2pdma_free_mappings(p2pdma->inode->i_mapping); > > With the series from Felix getting close this should get updated to > not set pte_devmap and use proper natural refcounting without any of > this stuff. Can you send a link? I'm not sure what you are referring to. Thanks, Logan
On Fri, May 27, 2022 at 09:35:07AM -0600, Logan Gunthorpe wrote: > > > On 2022-05-27 06:55, Jason Gunthorpe wrote: > > On Thu, Apr 07, 2022 at 09:47:16AM -0600, Logan Gunthorpe wrote: > >> +static void pci_p2pdma_unmap_mappings(void *data) > >> +{ > >> + struct pci_dev *pdev = data; > >> + struct pci_p2pdma *p2pdma = rcu_dereference_protected(pdev->p2pdma, 1); > >> + > >> + /* Ensure no new pages can be allocated in mappings */ > >> + p2pdma->active = false; > >> + synchronize_rcu(); > >> + > >> + unmap_mapping_range(p2pdma->inode->i_mapping, 0, 0, 1); > >> + > >> + /* > >> + * On some architectures, TLB flushes are done with call_rcu() > >> + * so to ensure GUP fast is done with the pages, call synchronize_rcu() > >> + * before freeing them. > >> + */ > >> + synchronize_rcu(); > >> + pci_p2pdma_free_mappings(p2pdma->inode->i_mapping); > > > > With the series from Felix getting close this should get updated to > > not set pte_devmap and use proper natural refcounting without any of > > this stuff. > > Can you send a link? I'm not sure what you are referring to. IIRC this is the last part: https://lore.kernel.org/linux-mm/20220524190632.3304-1-alex.sierra@amd.com/ And the earlier bit with Christoph's pieces looks like it might get merged to v5.19.. The general idea is once pte_devmap is not set then all the refcounting works the way it should. This is what all new ZONE_DEVICE users should do.. Jason
On 2022-05-27 13:03, Jason Gunthorpe wrote: > On Fri, May 27, 2022 at 09:35:07AM -0600, Logan Gunthorpe wrote: >> >> >> On 2022-05-27 06:55, Jason Gunthorpe wrote: >>> On Thu, Apr 07, 2022 at 09:47:16AM -0600, Logan Gunthorpe wrote: >>>> +static void pci_p2pdma_unmap_mappings(void *data) >>>> +{ >>>> + struct pci_dev *pdev = data; >>>> + struct pci_p2pdma *p2pdma = rcu_dereference_protected(pdev->p2pdma, 1); >>>> + >>>> + /* Ensure no new pages can be allocated in mappings */ >>>> + p2pdma->active = false; >>>> + synchronize_rcu(); >>>> + >>>> + unmap_mapping_range(p2pdma->inode->i_mapping, 0, 0, 1); >>>> + >>>> + /* >>>> + * On some architectures, TLB flushes are done with call_rcu() >>>> + * so to ensure GUP fast is done with the pages, call synchronize_rcu() >>>> + * before freeing them. >>>> + */ >>>> + synchronize_rcu(); >>>> + pci_p2pdma_free_mappings(p2pdma->inode->i_mapping); >>> >>> With the series from Felix getting close this should get updated to >>> not set pte_devmap and use proper natural refcounting without any of >>> this stuff. >> >> Can you send a link? I'm not sure what you are referring to. > > IIRC this is the last part: > > https://lore.kernel.org/linux-mm/20220524190632.3304-1-alex.sierra@amd.com/ > > And the earlier bit with Christoph's pieces looks like it might get > merged to v5.19.. > > The general idea is once pte_devmap is not set then all the > refcounting works the way it should. This is what all new ZONE_DEVICE > users should do.. Ok, I don't actually follow how those patches relate to this. Based on your description I guess I don't need to set PFN_DEV and perhaps not use vmf_insert_mixed()? And then just use vm_normal_page()? But the refcounting of the pages seemed like it was already sane to me, unless you mean that the code no longer has to synchronize_rcu() before returning the pages... that would be spectacular and clean things up a lot (plus fix an annoying issue where if you use then free all the memory you can't allocate new memory for an indeterminate amount of time). Logan
On Fri, May 27, 2022 at 04:41:08PM -0600, Logan Gunthorpe wrote: > > > > IIRC this is the last part: > > > > https://lore.kernel.org/linux-mm/20220524190632.3304-1-alex.sierra@amd.com/ > > > > And the earlier bit with Christoph's pieces looks like it might get > > merged to v5.19.. > > > > The general idea is once pte_devmap is not set then all the > > refcounting works the way it should. This is what all new ZONE_DEVICE > > users should do.. > > Ok, I don't actually follow how those patches relate to this. > > Based on your description I guess I don't need to set PFN_DEV and Yes > perhaps not use vmf_insert_mixed()? And then just use vm_normal_page()? I'm not sure ATM the best function to use, but yes, a function that doesn't set PFN_DEV is needed here. > But the refcounting of the pages seemed like it was already sane to me, > unless you mean that the code no longer has to synchronize_rcu() before > returning the pages... Right. It also doesn't need to call unmap range or keep track of the inode, or do any of that stuff unless it really needs mmap revokation semantics (which I doubt this use case does) unmap range was only necessary because the refcounting is wrong - since the pte's don't hold a ref on the page in PFN_DEV mode it is necessary to wipe all the PTE explicitly before going ahead to decrement the refcount on this path. Just stuff the pages into the mmap, and your driver unprobe will automatically block until all the mmaps are closed - no different than having an open file descriptor or something. Jason
On 2022-06-01 18:00, Jason Gunthorpe wrote: > On Fri, May 27, 2022 at 04:41:08PM -0600, Logan Gunthorpe wrote: >>> >>> IIRC this is the last part: >>> >>> https://lore.kernel.org/linux-mm/20220524190632.3304-1-alex.sierra@amd.com/ >>> >>> And the earlier bit with Christoph's pieces looks like it might get >>> merged to v5.19.. >>> >>> The general idea is once pte_devmap is not set then all the >>> refcounting works the way it should. This is what all new ZONE_DEVICE >>> users should do.. >> >> Ok, I don't actually follow how those patches relate to this. >> >> Based on your description I guess I don't need to set PFN_DEV and > > Yes > >> perhaps not use vmf_insert_mixed()? And then just use vm_normal_page()? > > I'm not sure ATM the best function to use, but yes, a function that > doesn't set PFN_DEV is needed here. > >> But the refcounting of the pages seemed like it was already sane to me, >> unless you mean that the code no longer has to synchronize_rcu() before >> returning the pages... > > Right. It also doesn't need to call unmap range or keep track of the > inode, or do any of that stuff unless it really needs mmap revokation > semantics (which I doubt this use case does) > > unmap range was only necessary because the refcounting is wrong - > since the pte's don't hold a ref on the page in PFN_DEV mode it is > necessary to wipe all the PTE explicitly before going ahead to > decrement the refcount on this path. > > Just stuff the pages into the mmap, and your driver unprobe will > automatically block until all the mmaps are closed - no different than > having an open file descriptor or something. Oh is that what we want? With the current method the mmaps are unmapped on unbind so that it doesn't block indefinitely. It seems more typical for resources to be dropped quickly on unbind and processes that are using them will get an error on next use. Logan
On Thu, Jun 02, 2022 at 10:16:10AM -0600, Logan Gunthorpe wrote: > > Just stuff the pages into the mmap, and your driver unprobe will > > automatically block until all the mmaps are closed - no different than > > having an open file descriptor or something. > > Oh is that what we want? Yes, it is the typical case - eg if you have a sysfs file open unbind hangs indefinitely. Many drivers can't unbind while they have open file descriptors/etc. A couple drivers go out of their way to allow unbinding while a live userspace exists but this can get complicated. Usually there should be a good reason. The module will already be refcounted anyhow because the mmap points to a char file which holds a module reference - meaning a simple rmmod of the driver shouldn't work already.. Jason
On 2022-06-02 10:30, Jason Gunthorpe wrote: > On Thu, Jun 02, 2022 at 10:16:10AM -0600, Logan Gunthorpe wrote: > >>> Just stuff the pages into the mmap, and your driver unprobe will >>> automatically block until all the mmaps are closed - no different than >>> having an open file descriptor or something. >> >> Oh is that what we want? > > Yes, it is the typical case - eg if you have a sysfs file open unbind > hangs indefinitely. Many drivers can't unbind while they have open file > descriptors/etc. > > A couple drivers go out of their way to allow unbinding while a live > userspace exists but this can get complicated. Usually there should be > a good reason. This is not my experience. All the drivers I've worked with do not block unbind with open file descriptors (at least for char devices). I know, for example, that having a file descriptor open of /dev/nvmeX does not cause unbinding to block. I figured this was the expectation as the userspace process doing the unbind won't be able to be interrupted seeing there's no way to fail on that path. Though, it certainly would make things a lot easier if the unbind can block indefinitely as it usually requires some complicated locking. Do you have an example of this? What mechanisms are developers using to block unbind with open file descriptors? Logan
On 2022-06-02 10:30, Jason Gunthorpe wrote: > On Thu, Jun 02, 2022 at 10:16:10AM -0600, Logan Gunthorpe wrote: > >>> Just stuff the pages into the mmap, and your driver unprobe will >>> automatically block until all the mmaps are closed - no different than >>> having an open file descriptor or something. >> >> Oh is that what we want? > > Yes, it is the typical case - eg if you have a sysfs file open unbind > hangs indefinitely. Many drivers can't unbind while they have open file > descriptors/etc. > > A couple drivers go out of their way to allow unbinding while a live > userspace exists but this can get complicated. Usually there should be > a good reason. > > The module will already be refcounted anyhow because the mmap points > to a char file which holds a module reference - meaning a simple rmmod > of the driver shouldn't work already.. Also, I just tried it... If I open a sysfs file for an nvme device (ie. /sys/class/nvme/nvme4/cntlid) and unbind the device, it does not block. A subsequent read on that file descriptor returns ENODEV. Which is what I would have expected. Logan
On Thu, Jun 02, 2022 at 10:45:55AM -0600, Logan Gunthorpe wrote: > > > > On 2022-06-02 10:30, Jason Gunthorpe wrote: > > On Thu, Jun 02, 2022 at 10:16:10AM -0600, Logan Gunthorpe wrote: > > > >>> Just stuff the pages into the mmap, and your driver unprobe will > >>> automatically block until all the mmaps are closed - no different than > >>> having an open file descriptor or something. > >> > >> Oh is that what we want? > > > > Yes, it is the typical case - eg if you have a sysfs file open unbind > > hangs indefinitely. Many drivers can't unbind while they have open file > > descriptors/etc. > > > > A couple drivers go out of their way to allow unbinding while a live > > userspace exists but this can get complicated. Usually there should be > > a good reason. > > This is not my experience. All the drivers I've worked with do not block > unbind with open file descriptors (at least for char devices). I know, > for example, that having a file descriptor open of /dev/nvmeX does not > cause unbinding to block. So there are lots of bugs in the kernel, and I've seen many drivers that think calling cdev_device_del() is all they need to do - and then happily allow cdev ioctl's/etc on a de-initialized driver struct. Drivers that do take care of this usually have to put a lock around all their fops to serialize against unbind. RDMA uses SRCU, iirc TPM used a rwlock. But this is tricky and hurts fops performance. I don't know what nvme did to protect against this, I didn't notice an obvious lock. > I figured this was the expectation as the userspace process doing > the unbind won't be able to be interrupted seeing there's no way to > fail on that path. Though, it certainly would make things a lot > easier if the unbind can block indefinitely as it usually requires > some complicated locking. As I said, this is what sysfs does today and I don't see that ever changing. If you userspace has a sysfs file open then the driver unbind hangs until the file is closed. So, doing as bad as sysfs seems like a reasonable baseline to me. > Do you have an example of this? What mechanisms are developers using to > block unbind with open file descriptors? Sysfs maintains a refcount with a bias that is basically a fancied rwlock. Most places use some kind of refcount triggering a completion. Sleep on the completion until refcount is 0 on unbind kind of thing. Jason
On Thu, Jun 02, 2022 at 10:49:15AM -0600, Logan Gunthorpe wrote: > > > On 2022-06-02 10:30, Jason Gunthorpe wrote: > > On Thu, Jun 02, 2022 at 10:16:10AM -0600, Logan Gunthorpe wrote: > > > >>> Just stuff the pages into the mmap, and your driver unprobe will > >>> automatically block until all the mmaps are closed - no different than > >>> having an open file descriptor or something. > >> > >> Oh is that what we want? > > > > Yes, it is the typical case - eg if you have a sysfs file open unbind > > hangs indefinitely. Many drivers can't unbind while they have open file > > descriptors/etc. > > > > A couple drivers go out of their way to allow unbinding while a live > > userspace exists but this can get complicated. Usually there should be > > a good reason. > > > > The module will already be refcounted anyhow because the mmap points > > to a char file which holds a module reference - meaning a simple rmmod > > of the driver shouldn't work already.. > > Also, I just tried it... If I open a sysfs file for an nvme device (ie. > /sys/class/nvme/nvme4/cntlid) and unbind the device, it does not block. > A subsequent read on that file descriptor returns ENODEV. Which is what > I would have expected. Oh interesting, this has been changed since years ago when I last looked, the kernfs_get_active() is now more narrowed than it once was. So manybe sysfs isn't the same concern it used to be! Thanks, Jason
On 2022-06-02 11:28, Jason Gunthorpe wrote: > On Thu, Jun 02, 2022 at 10:49:15AM -0600, Logan Gunthorpe wrote: >> >> >> On 2022-06-02 10:30, Jason Gunthorpe wrote: >>> On Thu, Jun 02, 2022 at 10:16:10AM -0600, Logan Gunthorpe wrote: >>> >>>>> Just stuff the pages into the mmap, and your driver unprobe will >>>>> automatically block until all the mmaps are closed - no different than >>>>> having an open file descriptor or something. >>>> >>>> Oh is that what we want? >>> >>> Yes, it is the typical case - eg if you have a sysfs file open unbind >>> hangs indefinitely. Many drivers can't unbind while they have open file >>> descriptors/etc. >>> >>> A couple drivers go out of their way to allow unbinding while a live >>> userspace exists but this can get complicated. Usually there should be >>> a good reason. >>> >>> The module will already be refcounted anyhow because the mmap points >>> to a char file which holds a module reference - meaning a simple rmmod >>> of the driver shouldn't work already.. >> >> Also, I just tried it... If I open a sysfs file for an nvme device (ie. >> /sys/class/nvme/nvme4/cntlid) and unbind the device, it does not block. >> A subsequent read on that file descriptor returns ENODEV. Which is what >> I would have expected. > > Oh interesting, this has been changed since years ago when I last > looked, the kernfs_get_active() is now more narrowed than it once > was. So manybe sysfs isn't the same concern it used to be! Yeah, so I really think *not* blocking unbind indefinitely is the better approach here. It's what has always been done with device dax, etc. mmaps in userspace processes get unmapped and will fault with SIGBUS on next access and unbind will actually unbind the device relatively promptly. Userspace processes can fail or try to handle the device going away gracefully. Logan
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 4d3cab9da748..cce4c7b6dd75 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -17,14 +17,19 @@ #include <linux/genalloc.h> #include <linux/memremap.h> #include <linux/percpu-refcount.h> +#include <linux/pfn_t.h> +#include <linux/pseudo_fs.h> #include <linux/random.h> #include <linux/seq_buf.h> #include <linux/xarray.h> +#include <uapi/linux/magic.h> struct pci_p2pdma { struct gen_pool *pool; bool p2pmem_published; struct xarray map_types; + struct inode *inode; + bool active; }; struct pci_p2pdma_pagemap { @@ -33,6 +38,17 @@ struct pci_p2pdma_pagemap { u64 bus_offset; }; +struct pci_p2pdma_map { + struct kref ref; + struct rcu_head rcu; + struct pci_dev *pdev; + struct inode *inode; + size_t len; + + spinlock_t kaddr_lock; + void *kaddr; +}; + static struct pci_p2pdma_pagemap *to_p2p_pgmap(struct dev_pagemap *pgmap) { return container_of(pgmap, struct pci_p2pdma_pagemap, pgmap); @@ -101,6 +117,38 @@ static const struct attribute_group p2pmem_group = { .name = "p2pmem", }; +/* + * P2PDMA internal mount + * Fake an internal VFS mount-point in order to allocate struct address_space + * mappings to remove VMAs on unbind events. + */ +static int pci_p2pdma_fs_cnt; +static struct vfsmount *pci_p2pdma_fs_mnt; + +static int pci_p2pdma_fs_init_fs_context(struct fs_context *fc) +{ + return init_pseudo(fc, P2PDMA_MAGIC) ? 0 : -ENOMEM; +} + +static struct file_system_type pci_p2pdma_fs_type = { + .name = "p2dma", + .owner = THIS_MODULE, + .init_fs_context = pci_p2pdma_fs_init_fs_context, + .kill_sb = kill_anon_super, +}; + +static void p2pdma_page_free(struct page *page) +{ + struct pci_p2pdma_pagemap *pgmap = to_p2p_pgmap(page->pgmap); + + gen_pool_free(pgmap->provider->p2pdma->pool, + (uintptr_t)page_to_virt(page), PAGE_SIZE); +} + +static const struct dev_pagemap_ops p2pdma_pgmap_ops = { + .page_free = p2pdma_page_free, +}; + static void pci_p2pdma_release(void *data) { struct pci_dev *pdev = data; @@ -117,6 +165,9 @@ static void pci_p2pdma_release(void *data) gen_pool_destroy(p2pdma->pool); sysfs_remove_group(&pdev->dev.kobj, &p2pmem_group); xa_destroy(&p2pdma->map_types); + + iput(p2pdma->inode); + simple_release_fs(&pci_p2pdma_fs_mnt, &pci_p2pdma_fs_cnt); } static int pci_p2pdma_setup(struct pci_dev *pdev) @@ -134,17 +185,32 @@ static int pci_p2pdma_setup(struct pci_dev *pdev) if (!p2p->pool) goto out; - error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_release, pdev); + error = simple_pin_fs(&pci_p2pdma_fs_type, &pci_p2pdma_fs_mnt, + &pci_p2pdma_fs_cnt); if (error) goto out_pool_destroy; + p2p->inode = alloc_anon_inode(pci_p2pdma_fs_mnt->mnt_sb); + if (IS_ERR(p2p->inode)) { + error = -ENOMEM; + goto out_unpin_fs; + } + + error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_release, pdev); + if (error) + goto out_put_inode; + error = sysfs_create_group(&pdev->dev.kobj, &p2pmem_group); if (error) - goto out_pool_destroy; + goto out_put_inode; rcu_assign_pointer(pdev->p2pdma, p2p); return 0; +out_put_inode: + iput(p2p->inode); +out_unpin_fs: + simple_release_fs(&pci_p2pdma_fs_mnt, &pci_p2pdma_fs_cnt); out_pool_destroy: gen_pool_destroy(p2p->pool); out: @@ -152,6 +218,54 @@ static int pci_p2pdma_setup(struct pci_dev *pdev) return error; } +static void pci_p2pdma_map_free_pages(struct pci_p2pdma_map *pmap) +{ + int i; + + if (!pmap->kaddr) + return; + + for (i = 0; i < pmap->len; i += PAGE_SIZE) + put_page(virt_to_page(pmap->kaddr + i)); + + pmap->kaddr = NULL; +} + +static void pci_p2pdma_free_mappings(struct address_space *mapping) +{ + struct vm_area_struct *vma; + + i_mmap_lock_write(mapping); + if (RB_EMPTY_ROOT(&mapping->i_mmap.rb_root)) + goto out; + + vma_interval_tree_foreach(vma, &mapping->i_mmap, 0, -1) + pci_p2pdma_map_free_pages(vma->vm_private_data); + +out: + i_mmap_unlock_write(mapping); +} + +static void pci_p2pdma_unmap_mappings(void *data) +{ + struct pci_dev *pdev = data; + struct pci_p2pdma *p2pdma = rcu_dereference_protected(pdev->p2pdma, 1); + + /* Ensure no new pages can be allocated in mappings */ + p2pdma->active = false; + synchronize_rcu(); + + unmap_mapping_range(p2pdma->inode->i_mapping, 0, 0, 1); + + /* + * On some architectures, TLB flushes are done with call_rcu() + * so to ensure GUP fast is done with the pages, call synchronize_rcu() + * before freeing them. + */ + synchronize_rcu(); + pci_p2pdma_free_mappings(p2pdma->inode->i_mapping); +} + /** * pci_p2pdma_add_resource - add memory for use as p2p memory * @pdev: the device to add the memory to @@ -198,6 +312,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, pgmap->range.end = pgmap->range.start + size - 1; pgmap->nr_range = 1; pgmap->type = MEMORY_DEVICE_PCI_P2PDMA; + pgmap->ops = &p2pdma_pgmap_ops; p2p_pgmap->provider = pdev; p2p_pgmap->bus_offset = pci_bus_address(pdev, bar) - @@ -209,6 +324,11 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, goto pgmap_free; } + error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_unmap_mappings, + pdev); + if (error) + goto pages_free; + p2pdma = rcu_dereference_protected(pdev->p2pdma, 1); error = gen_pool_add_owner(p2pdma->pool, (unsigned long)addr, pci_bus_address(pdev, bar) + offset, @@ -217,6 +337,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, if (error) goto pages_free; + p2pdma->active = true; pci_info(pdev, "added peer-to-peer DMA memory %#llx-%#llx\n", pgmap->range.start, pgmap->range.end); @@ -1018,3 +1139,218 @@ ssize_t pci_p2pdma_enable_show(char *page, struct pci_dev *p2p_dev, return sprintf(page, "%s\n", pci_name(p2p_dev)); } EXPORT_SYMBOL_GPL(pci_p2pdma_enable_show); + +static struct pci_p2pdma_map *pci_p2pdma_map_alloc(struct pci_dev *pdev, + size_t len) +{ + struct pci_p2pdma_map *pmap; + + pmap = kzalloc(sizeof(*pmap), GFP_KERNEL); + if (!pmap) + return NULL; + + kref_init(&pmap->ref); + pmap->pdev = pci_dev_get(pdev); + pmap->len = len; + + return pmap; +} + +static void pci_p2pdma_map_free(struct rcu_head *rcu) +{ + struct pci_p2pdma_map *pmap = + container_of(rcu, struct pci_p2pdma_map, rcu); + + pci_p2pdma_map_free_pages(pmap); + kfree(pmap); +} + +static void pci_p2pdma_map_release(struct kref *ref) +{ + struct pci_p2pdma_map *pmap = + container_of(ref, struct pci_p2pdma_map, ref); + + iput(pmap->inode); + simple_release_fs(&pci_p2pdma_fs_mnt, &pci_p2pdma_fs_cnt); + pci_dev_put(pmap->pdev); + + if (pmap->kaddr) { + /* + * Make sure to wait for the TLB flush (which some + * architectures do using call_rcu()) before returning the + * pages to the genalloc. This ensures the pages are not reused + * before GUP-fast is finished with them. So the mapping is + * freed using call_rcu() seeing adding synchronize_rcu() to + * the munmap path can cause long delays on large systems + * during process cleanup. + */ + call_rcu(&pmap->rcu, pci_p2pdma_map_free); + return; + } + + /* + * If there are no pages, just free the object immediately. There + * are no more references to it so there is nothing that can race + * with adding the pages. + */ + pci_p2pdma_map_free(&pmap->rcu); +} + +static void pci_p2pdma_vma_open(struct vm_area_struct *vma) +{ + struct pci_p2pdma_map *pmap = vma->vm_private_data; + + kref_get(&pmap->ref); +} + +static void pci_p2pdma_vma_close(struct vm_area_struct *vma) +{ + struct pci_p2pdma_map *pmap = vma->vm_private_data; + + kref_put(&pmap->ref, pci_p2pdma_map_release); +} + +static bool pci_p2pdma_vma_alloc(struct pci_p2pdma_map *pmap) +{ + struct pci_p2pdma *p2pdma; + bool ret = true; + void *kaddr; + + spin_lock(&pmap->kaddr_lock); + if (pmap->kaddr) + goto out_spin_unlock; + + rcu_read_lock(); + ret = false; + p2pdma = rcu_dereference(pmap->pdev->p2pdma); + if (!p2pdma) + goto out_rcu_unlock; + + if (!p2pdma->active) + goto out_rcu_unlock; + + kaddr = (void *)gen_pool_alloc(p2pdma->pool, pmap->len); + if (!kaddr) { + pci_err(pmap->pdev, + "No free P2PDMA memory for userspace mmap\n"); + goto out_rcu_unlock; + } + + WRITE_ONCE(pmap->kaddr, kaddr); + ret = true; + +out_rcu_unlock: + rcu_read_unlock(); +out_spin_unlock: + spin_unlock(&pmap->kaddr_lock); + return ret; +} + +static vm_fault_t pci_p2pdma_vma_fault(struct vm_fault *vmf) +{ + struct pci_p2pdma_map *pmap = vmf->vma->vm_private_data; + void *vaddr; + pfn_t pfn; + + if (!READ_ONCE(pmap->kaddr)) { + if (!pci_p2pdma_vma_alloc(pmap)) + return VM_FAULT_SIGBUS; + } + + vaddr = pmap->kaddr + vmf->address - vmf->vma->vm_start; + pfn = phys_to_pfn_t(virt_to_phys(vaddr), PFN_DEV | PFN_MAP); + + return vmf_insert_mixed(vmf->vma, vmf->address, pfn); +} +static const struct vm_operations_struct pci_p2pdma_vmops = { + .open = pci_p2pdma_vma_open, + .close = pci_p2pdma_vma_close, + .fault = pci_p2pdma_vma_fault, +}; + +/** + * pci_p2pdma_file_open - setup file mapping to store P2PMEM VMAs + * @pdev: the device to allocate memory from + * @vma: the userspace vma to map the memory to + * + * Set f_mapping of the file to the p2pdma inode so that mappings + * are can be torn down on device unbind. + * + * Returns 0 on success, or a negative error code on failure + */ +void pci_p2pdma_file_open(struct pci_dev *pdev, struct file *file) +{ + struct pci_p2pdma *p2pdma; + + rcu_read_lock(); + p2pdma = rcu_dereference(pdev->p2pdma); + if (p2pdma) + file->f_mapping = p2pdma->inode->i_mapping; + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(pci_p2pdma_file_open); + +/** + * pci_mmap_p2pmem - setup an mmap region to be backed with P2PDMA memory + * that was registered with pci_p2pdma_add_resource() + * @pdev: the device to allocate memory from + * @vma: the userspace vma to map the memory to + * + * The file must call pci_p2pdma_mmap_file_open() in its open() operation. + * + * Returns 0 on success, or a negative error code on failure + */ +int pci_mmap_p2pmem(struct pci_dev *pdev, struct vm_area_struct *vma) +{ + struct pci_p2pdma_map *pmap; + struct pci_p2pdma *p2pdma; + int ret; + + /* prevent private mappings from being established */ + if ((vma->vm_flags & VM_MAYSHARE) != VM_MAYSHARE) { + pci_info_ratelimited(pdev, + "%s: fail, attempted private mapping\n", + current->comm); + return -EINVAL; + } + + if (vma->vm_pgoff) { + pci_info_ratelimited(pdev, + "%s: fail, attempted mapping with non-zero offset\n", + current->comm); + return -EINVAL; + } + + pmap = pci_p2pdma_map_alloc(pdev, vma->vm_end - vma->vm_start); + if (!pmap) + return -ENOMEM; + + spin_lock_init(&pmap->kaddr_lock); + rcu_read_lock(); + p2pdma = rcu_dereference(pdev->p2pdma); + if (!p2pdma) { + ret = -ENODEV; + goto out; + } + + ret = simple_pin_fs(&pci_p2pdma_fs_type, &pci_p2pdma_fs_mnt, + &pci_p2pdma_fs_cnt); + if (ret) + goto out; + + ihold(p2pdma->inode); + pmap->inode = p2pdma->inode; + rcu_read_unlock(); + + vma->vm_flags |= VM_MIXEDMAP; + vma->vm_private_data = pmap; + vma->vm_ops = &pci_p2pdma_vmops; + + return 0; + +out: + rcu_read_unlock(); + kfree(pmap); + return ret; +} +EXPORT_SYMBOL_GPL(pci_mmap_p2pmem); diff --git a/include/linux/pci-p2pdma.h b/include/linux/pci-p2pdma.h index 2c07aa6b7665..040d79126463 100644 --- a/include/linux/pci-p2pdma.h +++ b/include/linux/pci-p2pdma.h @@ -34,6 +34,8 @@ int pci_p2pdma_enable_store(const char *page, struct pci_dev **p2p_dev, bool *use_p2pdma); ssize_t pci_p2pdma_enable_show(char *page, struct pci_dev *p2p_dev, bool use_p2pdma); +void pci_p2pdma_file_open(struct pci_dev *pdev, struct file *file); +int pci_mmap_p2pmem(struct pci_dev *pdev, struct vm_area_struct *vma); #else /* CONFIG_PCI_P2PDMA */ static inline int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, u64 offset) @@ -90,6 +92,15 @@ static inline ssize_t pci_p2pdma_enable_show(char *page, { return sprintf(page, "none\n"); } +static inline void pci_p2pdma_file_open(struct pci_dev *pdev, + struct file *file) +{ +} +static inline int pci_mmap_p2pmem(struct pci_dev *pdev, + struct vm_area_struct *vma) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_PCI_P2PDMA */ diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index f724129c0425..59ba2e60dc03 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -95,6 +95,7 @@ #define BPF_FS_MAGIC 0xcafe4a11 #define AAFS_MAGIC 0x5a3c69f0 #define ZONEFS_MAGIC 0x5a4f4653 +#define P2PDMA_MAGIC 0x70327064 /* Since UDF 2.01 is ISO 13346 based... */ #define UDF_SUPER_MAGIC 0x15013346