mmc: core: Drop reference counting of the bus_ops
authorUlf Hansson <ulf.hansson@linaro.org>
Fri, 12 Feb 2021 13:16:10 +0000 (14:16 +0100)
committerUlf Hansson <ulf.hansson@linaro.org>
Tue, 30 Mar 2021 09:41:58 +0000 (11:41 +0200)
When the mmc_rescan work is enabled for execution (host->rescan_disable),
it's the only instance per mmc host that is allowed to set/clear the
host->bus_ops pointer.

Besides the mmc_rescan work, there are a couple of scenarios when the
host->bus_ops pointer may be accessed. Typically, those can be described as
as below:

*)
Upper mmc driver layers (like the mmc block device driver or an SDIO
functional driver) needs to execute a host->bus_ops callback. This can be
considered as safe without having to use some special locking mechanism,
because they operate on top of the struct mmc_card. As long as there is a
card to operate upon, the mmc core guarantees that there is a host->bus_ops
assigned as well. Note that, upper layer mmc drivers are of course
responsible to clean up from themselves from their ->remove() callbacks,
otherwise things would fall apart anyways.

**)
Via the mmc host instance, we may need to force a removal of an inserted
mmc card. This happens when a mmc host driver gets unbind, for example. In
this case, we protect the host->bus_ops pointer from concurrent accesses,
by disabling the mmc_rescan work upfront (host->rescan_disable). See
mmc_stop_host() for example.

This said, it seems like the reference counting of the host->bus_ops
pointer at some point have become superfluous. As this is an old mechanism
of the mmc core, it a bit difficult to digest the history of when that
could have happened. However, let's drop the reference counting to avoid
unnecessary code-paths and lockings.

Cc: Pierre Ossman <pierre@ossman.eu>
Cc: Russell King <linux@armlinux.org.uk>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
Link: https://lore.kernel.org/r/20210212131610.236843-1-ulf.hansson@linaro.org
drivers/mmc/core/core.c
include/linux/mmc/host.h

index 99f0df9e04913b3bc4f07e0f1cb30f2bc59926ad..9c13f7a5269995cb0a7291d39a36757a6e932530 100644 (file)
@@ -1377,63 +1377,13 @@ void mmc_power_cycle(struct mmc_host *host, u32 ocr)
        mmc_power_up(host, ocr);
 }
 
-/*
- * Cleanup when the last reference to the bus operator is dropped.
- */
-static void __mmc_release_bus(struct mmc_host *host)
-{
-       WARN_ON(!host->bus_dead);
-
-       host->bus_ops = NULL;
-}
-
-/*
- * Increase reference count of bus operator
- */
-static inline void mmc_bus_get(struct mmc_host *host)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&host->lock, flags);
-       host->bus_refs++;
-       spin_unlock_irqrestore(&host->lock, flags);
-}
-
-/*
- * Decrease reference count of bus operator and free it if
- * it is the last reference.
- */
-static inline void mmc_bus_put(struct mmc_host *host)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&host->lock, flags);
-       host->bus_refs--;
-       if ((host->bus_refs == 0) && host->bus_ops)
-               __mmc_release_bus(host);
-       spin_unlock_irqrestore(&host->lock, flags);
-}
-
 /*
  * Assign a mmc bus handler to a host. Only one bus handler may control a
  * host at any given time.
  */
 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
 {
-       unsigned long flags;
-
-       WARN_ON(!host->claimed);
-
-       spin_lock_irqsave(&host->lock, flags);
-
-       WARN_ON(host->bus_ops);
-       WARN_ON(host->bus_refs);
-
        host->bus_ops = ops;
-       host->bus_refs = 1;
-       host->bus_dead = 0;
-
-       spin_unlock_irqrestore(&host->lock, flags);
 }
 
 /*
@@ -1441,18 +1391,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
  */
 void mmc_detach_bus(struct mmc_host *host)
 {
-       unsigned long flags;
-
-       WARN_ON(!host->claimed);
-       WARN_ON(!host->bus_ops);
-
-       spin_lock_irqsave(&host->lock, flags);
-
-       host->bus_dead = 1;
-
-       spin_unlock_irqrestore(&host->lock, flags);
-
-       mmc_bus_put(host);
+       host->bus_ops = NULL;
 }
 
 void _mmc_detect_change(struct mmc_host *host, unsigned long delay, bool cd_irq)
@@ -2244,32 +2183,15 @@ void mmc_rescan(struct work_struct *work)
                host->trigger_card_event = false;
        }
 
-       mmc_bus_get(host);
-
        /* Verify a registered card to be functional, else remove it. */
-       if (host->bus_ops && !host->bus_dead)
+       if (host->bus_ops)
                host->bus_ops->detect(host);
 
        host->detect_change = 0;
 
-       /*
-        * Let mmc_bus_put() free the bus/bus_ops if we've found that
-        * the card is no longer present.
-        */
-       mmc_bus_put(host);
-       mmc_bus_get(host);
-
        /* if there still is a card present, stop here */
-       if (host->bus_ops != NULL) {
-               mmc_bus_put(host);
+       if (host->bus_ops != NULL)
                goto out;
-       }
-
-       /*
-        * Only we can add a new handler, so it's safe to
-        * release the lock here.
-        */
-       mmc_bus_put(host);
 
        mmc_claim_host(host);
        if (mmc_card_is_removable(host) && host->ops->get_cd &&
@@ -2332,18 +2254,15 @@ void mmc_stop_host(struct mmc_host *host)
        /* clear pm flags now and let card drivers set them as needed */
        host->pm_flags = 0;
 
-       mmc_bus_get(host);
-       if (host->bus_ops && !host->bus_dead) {
+       if (host->bus_ops) {
                /* Calling bus_ops->remove() with a claimed host can deadlock */
                host->bus_ops->remove(host);
                mmc_claim_host(host);
                mmc_detach_bus(host);
                mmc_power_off(host);
                mmc_release_host(host);
-               mmc_bus_put(host);
                return;
        }
-       mmc_bus_put(host);
 
        mmc_claim_host(host);
        mmc_power_off(host);
index 26a3c7bc29ae57374e79836fb98ca4a451620062..a001ad2f5f23e3ec593899e119f4ac1f0b26d6f3 100644 (file)
@@ -423,7 +423,6 @@ struct mmc_host {
        /* group bitfields together to minimize padding */
        unsigned int            use_spi_crc:1;
        unsigned int            claimed:1;      /* host exclusively claimed */
-       unsigned int            bus_dead:1;     /* bus has been released */
        unsigned int            doing_init_tune:1; /* initial tuning in progress */
        unsigned int            can_retune:1;   /* re-tuning can be used */
        unsigned int            doing_retune:1; /* re-tuning in progress */
@@ -454,7 +453,6 @@ struct mmc_host {
        struct mmc_slot         slot;
 
        const struct mmc_bus_ops *bus_ops;      /* current bus driver */
-       unsigned int            bus_refs;       /* reference counter */
 
        unsigned int            sdio_irqs;
        struct task_struct      *sdio_irq_thread;