ieee1394: raw1394: prevent unloading of low-level driver
authorStefan Richter <stefanr@s5r6.in-berlin.de>
Sat, 3 Feb 2007 15:48:51 +0000 (16:48 +0100)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Thu, 8 Feb 2007 20:36:01 +0000 (21:36 +0100)
Unloading the low-level driver module of a FireWire host can lead to
all sorts of trouble if a raw1394 userspace client is using the host.
Just disallow it by incrementing the LLD's module reference count on
a RAW1394_REQ_SET_CARD write operation.  Decrement it when the file
is closed.

This feature wouldn't be relevant if "modprobe -r video1394" or
"modprobe -r dv1394" didn't automatically unload ohci1394 too.
http://bugzilla.kernel.org/show_bug.cgi?id=7701

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Signed-off-by: Dan Dennedy <dan@dennedy.org>
drivers/ieee1394/raw1394.c

index ad2108f27a04783643b84b4b9beb663e3e6f5cdd..a77a832828c84220d6d504c9be4d55101e5754ea 100644 (file)
@@ -636,27 +636,32 @@ static int state_initialized(struct file_info *fi, struct pending_request *req)
 
        case RAW1394_REQ_SET_CARD:
                spin_lock_irqsave(&host_info_lock, flags);
-               if (req->req.misc < host_count) {
-                       list_for_each_entry(hi, &host_info_list, list) {
-                               if (!req->req.misc--)
-                                       break;
-                       }
-                       get_device(&hi->host->device);  // XXX Need to handle failure case
-                       list_add_tail(&fi->list, &hi->file_info_list);
-                       fi->host = hi->host;
-                       fi->state = connected;
-
-                       req->req.error = RAW1394_ERROR_NONE;
-                       req->req.generation = get_hpsb_generation(fi->host);
-                       req->req.misc = (fi->host->node_id << 16)
-                           | fi->host->node_count;
-                       if (fi->protocol_version > 3) {
-                               req->req.misc |=
-                                   NODEID_TO_NODE(fi->host->irm_id) << 8;
-                       }
-               } else {
+               if (req->req.misc >= host_count) {
                        req->req.error = RAW1394_ERROR_INVALID_ARG;
+                       goto out_set_card;
                }
+               list_for_each_entry(hi, &host_info_list, list)
+                       if (!req->req.misc--)
+                               break;
+               get_device(&hi->host->device); /* FIXME handle failure case */
+               list_add_tail(&fi->list, &hi->file_info_list);
+
+               /* prevent unloading of the host's low-level driver */
+               if (!try_module_get(hi->host->driver->owner)) {
+                       req->req.error = RAW1394_ERROR_ABORTED;
+                       goto out_set_card;
+               }
+               WARN_ON(fi->host);
+               fi->host = hi->host;
+               fi->state = connected;
+
+               req->req.error = RAW1394_ERROR_NONE;
+               req->req.generation = get_hpsb_generation(fi->host);
+               req->req.misc = (fi->host->node_id << 16)
+                               | fi->host->node_count;
+               if (fi->protocol_version > 3)
+                       req->req.misc |= NODEID_TO_NODE(fi->host->irm_id) << 8;
+out_set_card:
                spin_unlock_irqrestore(&host_info_lock, flags);
 
                req->req.length = 0;
@@ -2955,6 +2960,11 @@ static int raw1394_release(struct inode *inode, struct file *file)
                put_device(&fi->host->device);
        }
 
+       spin_lock_irqsave(&host_info_lock, flags);
+       if (fi->host)
+               module_put(fi->host->driver->owner);
+       spin_unlock_irqrestore(&host_info_lock, flags);
+
        kfree(fi);
 
        return 0;