On Wed, Feb 15, 2017 at 12:12:47PM +0800, Shawn Lin wrote: > Hi Russell, > > On 2017/2/15 3:34, Russell King - ARM Linux wrote: > >On Tue, Feb 14, 2017 at 09:18:43AM -0700, Jens Axboe wrote: > >>The current situation seems like a bit of a mess. Why don't you have two > >>entry points, one for DMA and one for PIO. If the caller doesn't know if > >>he can use DMA, he'd better call the PIO variant. Either that, or audit > >>all callers and ensure they do the right thing wrt having a dma capable > >>buffer. > > > >It really shouldn't matter. MMC interfaces are just like USB - you > >have a host controller, which interfaces what is a multi-lane serial > >bus to the system. The SDIO card shouldn't care one bit whether > >the host controller is using DMA or PIO. > > > >However, I think that the DMA vs PIO thing is actually misleading here, > >that's really not the issue at all. > > > >Looking at the oops, I see it uses sdio_memcpy_toio(). Tracing that > >code leads me to here: > > > > for_each_sg(data.sg, sg_ptr, data.sg_len, i) { > > sg_set_page(sg_ptr, virt_to_page(buf + (i * > > seg_size)), > > min(seg_size, left_size), > > offset_in_page(buf + (i * > > seg_size))); > > > >so the buffer that is passed into sdio_memcpy_toio() gets passed into > >virt_to_page(). > > > >Firstly, the fact that it's passed to virt_to_page() means that "buf" > >must _only_ _ever_ be a lowmem address. It can't ever be a vmalloc > >address (virt_to_page() is invalid on anything but lowmem.) Just like > >certain kernel interfaces, passing pointers to memory of different types > >from the one intended by the interface produces invalid results, and > >that seems to be what's happening here. > > > >Secondly, it's a scatterlist, and scatterlists can be passed to DMA > >mapping operations, which also implies that _if_ a host driver decides > >to use DMA on it, the buffer better be DMA-able. > > > >Thirdly, while PIO may work (or even appear to work) because _maybe_ > >converting a vmalloc address to a ficticious struct page + offset, and > >then converting that back again _might_ result in hitting the correct > >memory, but it's not guaranteed to. > > > > [1]: > If no DMA involved, the host drivers usually use memcpy or readl/writel > to transfer the data between MMIO address and buffer coming from the > caller. So, is it also not guaranteed when using memcpy or readl to > transfer data between MMIO address and vmalloc/heap buffer?
The point here is, if buf is a vmalloc address: v = kmap_atomic(virt_to_page(buf)) v may be _anything_ at all. The kmap_atomic() will be done by the MMC host driver, the virt_to_page() is done by the code I quoted above. v may be a lowmem page address. v may be somewhere in userspace. v may be some device mapping. v may be (if you're lucky) the same address as "buf". There's no guarantees what it will be. Note that the scatterlist above does _not_ store the virtual address that was passed into it, but only the struct page, offset and length. So, drivers can not know what the original virtual address was. > >I suspect that virt_to_page() + kmap_atomic() is likely to try to > >dereference a struct page pointer that does not point at a legal entry > >in the memmap arrays, and result in scribbling over some random part > >of kernel memory. > > If that is the fact, so what I am concerned mostly is that by > seraching the APIs, sdio_writeb and sdio_readb, under the drivers/net > /wireless/, I could see almost all sdio based WLAN drivers passed in > a vmalloc area(actually when built as moudle, it should be located in > MODULE range which also be included as vmalloc area, no?) or heap > buffer. sdio_readb() and sdio_writeb() convey the data in the command stream, not the data stream. Firstly, sdio_writeb() takes the actual value to be written, so the memory storage is irrelevant (on many platforms, it will be passed as a value in CPU registers.) Eventually, it's written into the MMC command structure as the command argument, which typically ends up being written to a host controller register. In the case of sdio_readb(), the returned value comes from the command status, which is typically read from registers in the host controller. mmc_io_rw_direct_host() writes the value to the passed address. Hence, the host controller, again, never sees the address. > I assume my question[1] above is fine, then thanks to none of the mmc > host drivers use DMA for sdio_writeb and sdio_readb since it only > request one byte which didn't be fetched from host FIFO and the host > controller HW didn't support this kind of request to use DMA(but may be > not in the future). Otherwise, it may result in scribbling over some > random part of kernel memory. See above, your understanding of how sdio_readb() and sdio_writeb() is not correct. These are single value transfer functions, using mmc_io_rw_direct() as the underlying method of access, and do not transfer anything over the data lines. These functions are quite different from sdio_memcpy_toio(), sdio_memcpy_fromio(), sdio_readsb() and sdio_writesb(), which are multiple value transfer functions, and these perform the transfer using the data lines. These use sdio_io_rw_ext_helper(), which then uses mmc_io_rw_extended() to perform the transfer. The code I quoted in my original email is from mmc_io_rw_extended(). The command path is entirely separate from the data path in most MMC host controllers. The command path is commonly PIO, the data path is commonly DMA, but host controllers _may_ choose PIO for small transfers or when they have no DMA support. -- RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up according to speedtest.net.