Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dma-buf: fix dma_buf_export init order v2

The init order and resulting error handling in dma_buf_export
was pretty messy.

Subordinate objects like the file and the sysfs kernel objects
were initializing and wiring itself up with the object in the
wrong order resulting not only in complicating and partially
incorrect error handling, but also in publishing only halve
initialized DMA-buf objects.

Clean this up thoughtfully by allocating the file independent
of the DMA-buf object. Then allocate and initialize the DMA-buf
object itself, before publishing it through sysfs. If everything
works as expected the file is then connected with the DMA-buf
object and publish it through debugfs.

Also adds the missing dma_resv_fini() into the error handling.

v2: add some missing changes to dma_bug_getfile() and a missing NULL
check in dma_buf_file_release()

Signed-off-by: Christian König <christian.koenig@amd.com>
Reviewed-by: Michael J. Ruhl <michael.j.ruhl@intel.com>
Reviewed-by: T.J. Mercier <tjmercier@google.com>
Acked-by: Sumit Semwal <sumit.semwal@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20221209071535.933698-1-christian.koenig@amd.com

+48 -57
+2 -5
drivers/dma-buf/dma-buf-sysfs-stats.c
··· 168 168 kset_unregister(dma_buf_stats_kset); 169 169 } 170 170 171 - int dma_buf_stats_setup(struct dma_buf *dmabuf) 171 + int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file) 172 172 { 173 173 struct dma_buf_sysfs_entry *sysfs_entry; 174 174 int ret; 175 - 176 - if (!dmabuf || !dmabuf->file) 177 - return -EINVAL; 178 175 179 176 if (!dmabuf->exp_name) { 180 177 pr_err("exporter name must not be empty if stats needed\n"); ··· 189 192 190 193 /* create the directory for buffer stats */ 191 194 ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_ktype, NULL, 192 - "%lu", file_inode(dmabuf->file)->i_ino); 195 + "%lu", file_inode(file)->i_ino); 193 196 if (ret) 194 197 goto err_sysfs_dmabuf; 195 198
+2 -2
drivers/dma-buf/dma-buf-sysfs-stats.h
··· 13 13 int dma_buf_init_sysfs_statistics(void); 14 14 void dma_buf_uninit_sysfs_statistics(void); 15 15 16 - int dma_buf_stats_setup(struct dma_buf *dmabuf); 16 + int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file); 17 17 18 18 void dma_buf_stats_teardown(struct dma_buf *dmabuf); 19 19 #else ··· 25 25 26 26 static inline void dma_buf_uninit_sysfs_statistics(void) {} 27 27 28 - static inline int dma_buf_stats_setup(struct dma_buf *dmabuf) 28 + static inline int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file) 29 29 { 30 30 return 0; 31 31 }
+44 -50
drivers/dma-buf/dma-buf.c
··· 95 95 return -EINVAL; 96 96 97 97 dmabuf = file->private_data; 98 - 99 - mutex_lock(&db_list.lock); 100 - list_del(&dmabuf->list_node); 101 - mutex_unlock(&db_list.lock); 98 + if (dmabuf) { 99 + mutex_lock(&db_list.lock); 100 + list_del(&dmabuf->list_node); 101 + mutex_unlock(&db_list.lock); 102 + } 102 103 103 104 return 0; 104 105 } ··· 524 523 return file->f_op == &dma_buf_fops; 525 524 } 526 525 527 - static struct file *dma_buf_getfile(struct dma_buf *dmabuf, int flags) 526 + static struct file *dma_buf_getfile(size_t size, int flags) 528 527 { 529 528 static atomic64_t dmabuf_inode = ATOMIC64_INIT(0); 530 - struct file *file; 531 529 struct inode *inode = alloc_anon_inode(dma_buf_mnt->mnt_sb); 530 + struct file *file; 532 531 533 532 if (IS_ERR(inode)) 534 533 return ERR_CAST(inode); 535 534 536 - inode->i_size = dmabuf->size; 537 - inode_set_bytes(inode, dmabuf->size); 535 + inode->i_size = size; 536 + inode_set_bytes(inode, size); 538 537 539 538 /* 540 539 * The ->i_ino acquired from get_next_ino() is not unique thus ··· 548 547 flags, &dma_buf_fops); 549 548 if (IS_ERR(file)) 550 549 goto err_alloc_file; 551 - file->private_data = dmabuf; 552 - file->f_path.dentry->d_fsdata = dmabuf; 553 550 554 551 return file; 555 552 ··· 613 614 size_t alloc_size = sizeof(struct dma_buf); 614 615 int ret; 615 616 616 - if (!exp_info->resv) 617 - alloc_size += sizeof(struct dma_resv); 618 - else 619 - /* prevent &dma_buf[1] == dma_buf->resv */ 620 - alloc_size += 1; 621 - 622 - if (WARN_ON(!exp_info->priv 623 - || !exp_info->ops 624 - || !exp_info->ops->map_dma_buf 625 - || !exp_info->ops->unmap_dma_buf 626 - || !exp_info->ops->release)) { 617 + if (WARN_ON(!exp_info->priv || !exp_info->ops 618 + || !exp_info->ops->map_dma_buf 619 + || !exp_info->ops->unmap_dma_buf 620 + || !exp_info->ops->release)) 627 621 return ERR_PTR(-EINVAL); 628 - } 629 622 630 623 if (WARN_ON(exp_info->ops->cache_sgt_mapping && 631 624 (exp_info->ops->pin || exp_info->ops->unpin))) ··· 629 638 if (!try_module_get(exp_info->owner)) 630 639 return ERR_PTR(-ENOENT); 631 640 641 + file = dma_buf_getfile(exp_info->size, exp_info->flags); 642 + if (IS_ERR(file)) { 643 + ret = PTR_ERR(file); 644 + goto err_module; 645 + } 646 + 647 + if (!exp_info->resv) 648 + alloc_size += sizeof(struct dma_resv); 649 + else 650 + /* prevent &dma_buf[1] == dma_buf->resv */ 651 + alloc_size += 1; 632 652 dmabuf = kzalloc(alloc_size, GFP_KERNEL); 633 653 if (!dmabuf) { 634 654 ret = -ENOMEM; 635 - goto err_module; 655 + goto err_file; 636 656 } 637 657 638 658 dmabuf->priv = exp_info->priv; ··· 655 653 init_waitqueue_head(&dmabuf->poll); 656 654 dmabuf->cb_in.poll = dmabuf->cb_out.poll = &dmabuf->poll; 657 655 dmabuf->cb_in.active = dmabuf->cb_out.active = 0; 658 - 659 - if (!resv) { 660 - resv = (struct dma_resv *)&dmabuf[1]; 661 - dma_resv_init(resv); 662 - } 663 - dmabuf->resv = resv; 664 - 665 - file = dma_buf_getfile(dmabuf, exp_info->flags); 666 - if (IS_ERR(file)) { 667 - ret = PTR_ERR(file); 668 - goto err_dmabuf; 669 - } 670 - 671 - dmabuf->file = file; 672 - 673 656 mutex_init(&dmabuf->lock); 674 657 INIT_LIST_HEAD(&dmabuf->attachments); 658 + 659 + if (!resv) { 660 + dmabuf->resv = (struct dma_resv *)&dmabuf[1]; 661 + dma_resv_init(dmabuf->resv); 662 + } else { 663 + dmabuf->resv = resv; 664 + } 665 + 666 + ret = dma_buf_stats_setup(dmabuf, file); 667 + if (ret) 668 + goto err_dmabuf; 669 + 670 + file->private_data = dmabuf; 671 + file->f_path.dentry->d_fsdata = dmabuf; 672 + dmabuf->file = file; 675 673 676 674 mutex_lock(&db_list.lock); 677 675 list_add(&dmabuf->list_node, &db_list.head); 678 676 mutex_unlock(&db_list.lock); 679 677 680 - ret = dma_buf_stats_setup(dmabuf); 681 - if (ret) 682 - goto err_sysfs; 683 - 684 678 return dmabuf; 685 679 686 - err_sysfs: 687 - /* 688 - * Set file->f_path.dentry->d_fsdata to NULL so that when 689 - * dma_buf_release() gets invoked by dentry_ops, it exits 690 - * early before calling the release() dma_buf op. 691 - */ 692 - file->f_path.dentry->d_fsdata = NULL; 693 - fput(file); 694 680 err_dmabuf: 681 + if (!resv) 682 + dma_resv_fini(dmabuf->resv); 695 683 kfree(dmabuf); 684 + err_file: 685 + fput(file); 696 686 err_module: 697 687 module_put(exp_info->owner); 698 688 return ERR_PTR(ret);