Author: hselasky
Date: Wed May 31 12:02:59 2017
New Revision: 319316
URL: https://svnweb.freebsd.org/changeset/base/319316

Log:
  Fixes for refcounting "struct linux_file" in the LinuxKPI.
  
  - Allow "struct linux_file" to be refcounted when its "_file" member
    is NULL by using its "f_count" field. The reference counts are
    transferred to the file structure when the file descriptor is
    installed.
  
  - Add missing vdrop() calls for error cases during open().
  
  - Set the "_file" member of "struct linux_file" during open. This
  allows use of refcounting through get_file() and fput() with LinuxKPI
  character devices.
  
  MFC after:            1 week
  Sponsored by:         Mellanox Technologies

Modified:
  head/sys/compat/linuxkpi/common/include/linux/file.h
  head/sys/compat/linuxkpi/common/include/linux/fs.h
  head/sys/compat/linuxkpi/common/src/linux_compat.c

Modified: head/sys/compat/linuxkpi/common/include/linux/file.h
==============================================================================
--- head/sys/compat/linuxkpi/common/include/linux/file.h        Wed May 31 
10:32:13 2017        (r319315)
+++ head/sys/compat/linuxkpi/common/include/linux/file.h        Wed May 31 
12:02:59 2017        (r319316)
@@ -2,7 +2,7 @@
  * Copyright (c) 2010 Isilon Systems, Inc.
  * Copyright (c) 2010 iX Systems, Inc.
  * Copyright (c) 2010 Panasas, Inc.
- * Copyright (c) 2013-2015 Mellanox Technologies, Ltd.
+ * Copyright (c) 2013-2017 Mellanox Technologies, Ltd.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -60,19 +60,24 @@ linux_fget(unsigned int fd)
        return (struct linux_file *)file->f_data;
 }
 
+extern void linux_file_free(struct linux_file *filp);
+
 static inline void
 fput(struct linux_file *filp)
 {
-       if (filp->_file == NULL) {
-               kfree(filp);
-               return;
+       if (refcount_release(filp->_file == NULL ?
+           &filp->f_count : &filp->_file->f_count)) {
+               linux_file_free(filp);
        }
-       if (refcount_release(&filp->_file->f_count)) {
-               _fdrop(filp->_file, curthread);
-               kfree(filp);
-       }
 }
 
+static inline unsigned int
+file_count(struct linux_file *filp)
+{
+       return (filp->_file == NULL ?
+           filp->f_count : filp->_file->f_count);
+}
+
 static inline void
 put_unused_fd(unsigned int fd)
 {
@@ -106,6 +111,10 @@ fd_install(unsigned int fd, struct linux_file *filp)
        } else {
                filp->_file = file;
                finit(file, filp->f_mode, DTYPE_DEV, filp, &linuxfileops);
+
+               /* transfer reference count from "filp" to "file" */
+               while (refcount_release(&filp->f_count) == 0)
+                       refcount_acquire(&file->f_count);
        }
 
        /* drop the extra reference */
@@ -150,6 +159,8 @@ alloc_file(int mode, const struct file_operations *fop
        filp = kzalloc(sizeof(*filp), GFP_KERNEL);
        if (filp == NULL)
                return (NULL);
+
+       filp->f_count = 1;
        filp->f_op = fops;
        filp->f_mode = mode;
 
@@ -171,7 +182,7 @@ static inline struct fd fdget(unsigned int fd)
        return (struct fd){f};
 }
 
-#define        file    linux_file
-#define        fget    linux_fget
+#define        file            linux_file
+#define        fget(...)       linux_fget(__VA_ARGS__)
 
 #endif /* _LINUX_FILE_H_ */

Modified: head/sys/compat/linuxkpi/common/include/linux/fs.h
==============================================================================
--- head/sys/compat/linuxkpi/common/include/linux/fs.h  Wed May 31 10:32:13 
2017        (r319315)
+++ head/sys/compat/linuxkpi/common/include/linux/fs.h  Wed May 31 12:02:59 
2017        (r319316)
@@ -79,6 +79,7 @@ struct linux_file {
        struct selinfo  f_selinfo;
        struct sigio    *f_sigio;
        struct vnode    *f_vnode;
+       volatile u_int  f_count;
 };
 
 #define        file            linux_file
@@ -218,6 +219,14 @@ iminor(struct inode *inode)
 {
 
        return (minor(dev2unit(inode->v_rdev)));
+}
+
+static inline struct linux_file *
+get_file(struct linux_file *f)
+{
+
+       refcount_acquire(f->_file == NULL ? &f->f_count : &f->_file->f_count);
+       return (f);
 }
 
 static inline struct inode *

Modified: head/sys/compat/linuxkpi/common/src/linux_compat.c
==============================================================================
--- head/sys/compat/linuxkpi/common/src/linux_compat.c  Wed May 31 10:32:13 
2017        (r319315)
+++ head/sys/compat/linuxkpi/common/src/linux_compat.c  Wed May 31 12:02:59 
2017        (r319316)
@@ -401,6 +401,20 @@ linux_file_dtor(void *cdp)
        kfree(filp);
 }
 
+void
+linux_file_free(struct linux_file *filp)
+{
+       if (filp->_file == NULL) {
+               kfree(filp);
+       } else {
+               /*
+                * The close method of the character device or file
+                * will free the linux_file structure:
+                */
+               _fdrop(filp->_file, curthread);
+       }
+}
+
 static int
 linux_cdev_pager_populate(vm_object_t vm_obj, vm_pindex_t pidx, int fault_type,
     vm_prot_t max_prot, vm_pindex_t *first, vm_pindex_t *last)
@@ -593,9 +607,12 @@ linux_dev_open(struct cdev *dev, int oflags, int devty
        vhold(file->f_vnode);
        filp->f_vnode = file->f_vnode;
        linux_set_current(td);
+       filp->_file = file;
+
        if (filp->f_op->open) {
                error = -filp->f_op->open(file->f_vnode, filp);
                if (error) {
+                       vdrop(filp->f_vnode);
                        kfree(filp);
                        goto done;
                }
@@ -603,6 +620,7 @@ linux_dev_open(struct cdev *dev, int oflags, int devty
        error = devfs_set_cdevpriv(filp, linux_file_dtor);
        if (error) {
                filp->f_op->release(file->f_vnode, filp);
+               vdrop(filp->f_vnode);
                kfree(filp);
        }
 done:
@@ -622,8 +640,7 @@ linux_dev_close(struct cdev *dev, int fflag, int devty
        if ((error = devfs_get_cdevpriv((void **)&filp)) != 0)
                return (error);
        filp->f_flags = file->f_flag;
-        devfs_clear_cdevpriv();
-        
+       devfs_clear_cdevpriv();
 
        return (0);
 }
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to