1010#include <linux/module.h>
1111#include <linux/slab.h>
1212
13+ #include <uapi/fwctl/fwctl.h>
14+
1315enum {
1416 FWCTL_MAX_DEVICES = 4096 ,
1517};
@@ -18,20 +20,128 @@ static_assert(FWCTL_MAX_DEVICES < (1U << MINORBITS));
1820static dev_t fwctl_dev ;
1921static DEFINE_IDA (fwctl_ida );
2022
23+ struct fwctl_ucmd {
24+ struct fwctl_uctx * uctx ;
25+ void __user * ubuffer ;
26+ void * cmd ;
27+ u32 user_size ;
28+ };
29+
30+ /* On stack memory for the ioctl structs */
31+ union fwctl_ucmd_buffer {
32+ };
33+
34+ struct fwctl_ioctl_op {
35+ unsigned int size ;
36+ unsigned int min_size ;
37+ unsigned int ioctl_num ;
38+ int (* execute )(struct fwctl_ucmd * ucmd );
39+ };
40+
41+ #define IOCTL_OP (_ioctl , _fn , _struct , _last ) \
42+ [_IOC_NR(_ioctl) - FWCTL_CMD_BASE] = { \
43+ .size = sizeof(_struct) + \
44+ BUILD_BUG_ON_ZERO(sizeof(union fwctl_ucmd_buffer) < \
45+ sizeof(_struct)), \
46+ .min_size = offsetofend(_struct, _last), \
47+ .ioctl_num = _ioctl, \
48+ .execute = _fn, \
49+ }
50+ static const struct fwctl_ioctl_op fwctl_ioctl_ops [] = {
51+ };
52+
53+ static long fwctl_fops_ioctl (struct file * filp , unsigned int cmd ,
54+ unsigned long arg )
55+ {
56+ struct fwctl_uctx * uctx = filp -> private_data ;
57+ const struct fwctl_ioctl_op * op ;
58+ struct fwctl_ucmd ucmd = {};
59+ union fwctl_ucmd_buffer buf ;
60+ unsigned int nr ;
61+ int ret ;
62+
63+ nr = _IOC_NR (cmd );
64+ if ((nr - FWCTL_CMD_BASE ) >= ARRAY_SIZE (fwctl_ioctl_ops ))
65+ return - ENOIOCTLCMD ;
66+
67+ op = & fwctl_ioctl_ops [nr - FWCTL_CMD_BASE ];
68+ if (op -> ioctl_num != cmd )
69+ return - ENOIOCTLCMD ;
70+
71+ ucmd .uctx = uctx ;
72+ ucmd .cmd = & buf ;
73+ ucmd .ubuffer = (void __user * )arg ;
74+ ret = get_user (ucmd .user_size , (u32 __user * )ucmd .ubuffer );
75+ if (ret )
76+ return ret ;
77+
78+ if (ucmd .user_size < op -> min_size )
79+ return - EINVAL ;
80+
81+ ret = copy_struct_from_user (ucmd .cmd , op -> size , ucmd .ubuffer ,
82+ ucmd .user_size );
83+ if (ret )
84+ return ret ;
85+
86+ guard (rwsem_read )(& uctx -> fwctl -> registration_lock );
87+ if (!uctx -> fwctl -> ops )
88+ return - ENODEV ;
89+ return op -> execute (& ucmd );
90+ }
91+
2192static int fwctl_fops_open (struct inode * inode , struct file * filp )
2293{
2394 struct fwctl_device * fwctl =
2495 container_of (inode -> i_cdev , struct fwctl_device , cdev );
96+ int ret ;
97+
98+ guard (rwsem_read )(& fwctl -> registration_lock );
99+ if (!fwctl -> ops )
100+ return - ENODEV ;
101+
102+ struct fwctl_uctx * uctx __free (kfree ) =
103+ kzalloc (fwctl -> ops -> uctx_size , GFP_KERNEL_ACCOUNT );
104+ if (!uctx )
105+ return - ENOMEM ;
106+
107+ uctx -> fwctl = fwctl ;
108+ ret = fwctl -> ops -> open_uctx (uctx );
109+ if (ret )
110+ return ret ;
111+
112+ scoped_guard (mutex , & fwctl -> uctx_list_lock ) {
113+ list_add_tail (& uctx -> uctx_list_entry , & fwctl -> uctx_list );
114+ }
25115
26116 get_device (& fwctl -> dev );
27- filp -> private_data = fwctl ;
117+ filp -> private_data = no_free_ptr ( uctx ) ;
28118 return 0 ;
29119}
30120
121+ static void fwctl_destroy_uctx (struct fwctl_uctx * uctx )
122+ {
123+ lockdep_assert_held (& uctx -> fwctl -> uctx_list_lock );
124+ list_del (& uctx -> uctx_list_entry );
125+ uctx -> fwctl -> ops -> close_uctx (uctx );
126+ }
127+
31128static int fwctl_fops_release (struct inode * inode , struct file * filp )
32129{
33- struct fwctl_device * fwctl = filp -> private_data ;
130+ struct fwctl_uctx * uctx = filp -> private_data ;
131+ struct fwctl_device * fwctl = uctx -> fwctl ;
34132
133+ scoped_guard (rwsem_read , & fwctl -> registration_lock ) {
134+ /*
135+ * NULL ops means fwctl_unregister() has already removed the
136+ * driver and destroyed the uctx.
137+ */
138+ if (fwctl -> ops ) {
139+ guard (mutex )(& fwctl -> uctx_list_lock );
140+ fwctl_destroy_uctx (uctx );
141+ }
142+ }
143+
144+ kfree (uctx );
35145 fwctl_put (fwctl );
36146 return 0 ;
37147}
@@ -40,6 +150,7 @@ static const struct file_operations fwctl_fops = {
40150 .owner = THIS_MODULE ,
41151 .open = fwctl_fops_open ,
42152 .release = fwctl_fops_release ,
153+ .unlocked_ioctl = fwctl_fops_ioctl ,
43154};
44155
45156static void fwctl_device_release (struct device * device )
@@ -48,6 +159,7 @@ static void fwctl_device_release(struct device *device)
48159 container_of (device , struct fwctl_device , dev );
49160
50161 ida_free (& fwctl_ida , fwctl -> dev .devt - fwctl_dev );
162+ mutex_destroy (& fwctl -> uctx_list_lock );
51163 kfree (fwctl );
52164}
53165
@@ -71,9 +183,6 @@ _alloc_device(struct device *parent, const struct fwctl_ops *ops, size_t size)
71183 if (!fwctl )
72184 return NULL ;
73185
74- fwctl -> dev .class = & fwctl_class ;
75- fwctl -> dev .parent = parent ;
76-
77186 devnum = ida_alloc_max (& fwctl_ida , FWCTL_MAX_DEVICES - 1 , GFP_KERNEL );
78187 if (devnum < 0 )
79188 return NULL ;
@@ -82,6 +191,10 @@ _alloc_device(struct device *parent, const struct fwctl_ops *ops, size_t size)
82191 fwctl -> dev .class = & fwctl_class ;
83192 fwctl -> dev .parent = parent ;
84193
194+ init_rwsem (& fwctl -> registration_lock );
195+ mutex_init (& fwctl -> uctx_list_lock );
196+ INIT_LIST_HEAD (& fwctl -> uctx_list );
197+
85198 device_initialize (& fwctl -> dev );
86199 return_ptr (fwctl );
87200}
@@ -132,14 +245,34 @@ EXPORT_SYMBOL_NS_GPL(fwctl_register, "FWCTL");
132245 * Undoes fwctl_register(). On return no driver ops will be called. The
133246 * caller must still call fwctl_put() to free the fwctl.
134247 *
248+ * Unregister will return even if userspace still has file descriptors open.
249+ * This will call ops->close_uctx() on any open FDs and after return no driver
250+ * op will be called. The FDs remain open but all fops will return -ENODEV.
251+ *
135252 * The design of fwctl allows this sort of disassociation of the driver from the
136253 * subsystem primarily by keeping memory allocations owned by the core subsytem.
137254 * The fwctl_device and fwctl_uctx can both be freed without requiring a driver
138255 * callback. This allows the module to remain unlocked while FDs are open.
139256 */
140257void fwctl_unregister (struct fwctl_device * fwctl )
141258{
259+ struct fwctl_uctx * uctx ;
260+
142261 cdev_device_del (& fwctl -> cdev , & fwctl -> dev );
262+
263+ /* Disable and free the driver's resources for any still open FDs. */
264+ guard (rwsem_write )(& fwctl -> registration_lock );
265+ guard (mutex )(& fwctl -> uctx_list_lock );
266+ while ((uctx = list_first_entry_or_null (& fwctl -> uctx_list ,
267+ struct fwctl_uctx ,
268+ uctx_list_entry )))
269+ fwctl_destroy_uctx (uctx );
270+
271+ /*
272+ * The driver module may unload after this returns, the op pointer will
273+ * not be valid.
274+ */
275+ fwctl -> ops = NULL ;
143276}
144277EXPORT_SYMBOL_NS_GPL (fwctl_unregister , "FWCTL" );
145278
0 commit comments