[NEW PATCH] Re: The floppy driver and its DMA allocation problems

Alain Knaff (knaff@tux.org)
Sun, 11 Jan 1998 11:08:19 +0100


>> The patch at the end of the mail switches the floppy driver in DMA
>> less mode on boxes with more than 16 MB of memory.
>
>This seems less than ideal. Firstly it could check the floppy controller
>type before deciding,

Actually, the floppy controller type _is_ checked. I just didn't
mention it on the first line of the message (but only further down)

> secondly trying the DMA allocation then flipping to
>nodma if it cant get it gives performance most of the time

Correct. Here is a new version which only uses virtual DMA when the
allocation actually fails.

Regards,

Alain

diff -ur 2.1.78/linux/drivers/block/README.fd linux/drivers/block/README.fd
--- 2.1.78/linux/drivers/block/README.fd Tue Nov 18 00:13:48 1997
+++ linux/drivers/block/README.fd Fri Jan 9 22:10:43 1998
@@ -85,16 +85,20 @@
This is needed on HP Omnibooks, which don't have a workable
DMA channel for the floppy driver. This option is also useful
if you frequently get "Unable to allocate DMA memory" messages.
- Indeed, dma memory needs to be continuous in physical, and is
- thus harder to find, whereas non-dma buffers may be allocated
- in virtual memory. However, I advise against this if you have
- an FDC without a FIFO (8272A or 82072). 82072A and later are
- OK. You also need at least a 486 to use nodma.
+ Indeed, dma memory needs to be continuous in physical memory,
+ and is thus harder to find, whereas non-dma buffers may be
+ allocated in virtual memory. However, I advise against this if
+ you have an FDC without a FIFO (8272A or 82072). 82072A and
+ later are OK). You also need at least a 486 to use nodma.
If you use nodma mode, I suggest you also set the FIFO
threshold to 10 or lower, in order to limit the number of data
transfer interrupts.
-
- floppy=dma
+
+ If you have a FIFO-able FDC, the floppy driver automatically
+ falls back on non DMA mode if no DMA-able memory can be found.
+ If you want to avoid this, explicitely ask for 'yesdma'.
+
+ floppy=yesdma
Tells the floppy driver that a workable DMA channel is available
(the default).

diff -ur 2.1.78/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- 2.1.78/linux/drivers/block/floppy.c Tue Jan 6 19:07:27 1998
+++ linux/drivers/block/floppy.c Sun Jan 11 08:17:37 1998
@@ -148,11 +148,32 @@
#include <asm/io.h>
#include <asm/uaccess.h>

-static int use_virtual_dma=0; /* virtual DMA for Intel */
+static int can_use_virtual_dma=2;
+/* =======
+ * can use virtual DMA:
+ * 0 = use of virtual DMA disallowed by config
+ * 1 = use of virtual DMA prescribed by config
+ * 2 = no virtual DMA preference configured. By default try hard DMA,
+ * but fall back on virtual DMA when not enough memory available
+ */
+
+static int use_virtual_dma=0;
+/* =======
+ * use virtual DMA
+ * 0 using hard DMA
+ * 1 using virtual DMA
+ * This variable is set to virtual when a DMA mem problem arises, and
+ * reset back in floppy_grab_irq_and_dma.
+ * It is not safe to reset it in other circumstances, because the floppy
+ * driver may have several buffers in use at once, and we do currently not
+ * record each buffers capabilities
+ */
+
static unsigned short virtual_dma_port=0x3f0;
void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
static int set_dor(int fdc, char mask, char data);
static inline int __get_order(unsigned long size);
+#define K_64 0x10000 /* 64KB */
#include <asm/floppy.h>


@@ -189,6 +210,20 @@
#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
#endif

+static inline void fallback_on_nodma_alloc(char **addr, size_t l)
+{
+#ifdef FLOPPY_CAN_FALLBACK_ON_NODMA
+ if(*addr)
+ return; /* we have the memory */
+ if(can_use_virtual_dma != 2)
+ return; /* no fallback allowed */
+ printk("DMA memory shortage. Temporarily falling back on virtual DMA\n");
+ *addr = (char *) nodma_mem_alloc(l);
+#else
+ return;
+#endif
+}
+
/* End dma memory related stuff */

static unsigned int fake_change = 0;
@@ -258,7 +293,6 @@
*/
#define MAX_DISK_SIZE 4 /* 3984*/

-#define K_64 0x10000 /* 64KB */

/*
* globals used by 'result()'
@@ -1015,17 +1049,20 @@
FDCS->reset=1;
return;
}
- if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
- printk("DMA crossing 64-K boundary %p-%p\n",
- raw_cmd->kernel_data,
- raw_cmd->kernel_data + raw_cmd->length);
+#endif
+ INT_OFF;
+ fd_disable_dma();
+#ifdef fd_dma_setup
+ if(fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length,
+ (raw_cmd->flags & FD_RAW_READ)?
+ DMA_MODE_READ : DMA_MODE_WRITE,
+ FDCS->address) < 0) {
+ INT_ON;
cont->done(0);
FDCS->reset=1;
return;
}
-#endif
- INT_OFF;
- fd_disable_dma();
+#else
fd_clear_dma_ff();
fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
@@ -1034,6 +1071,7 @@
fd_set_dma_count(raw_cmd->length);
virtual_dma_port = FDCS->address;
fd_enable_dma();
+#endif
INT_ON;
floppy_disable_hlt();
}
@@ -1844,19 +1882,29 @@
DPRINT("calling disk change from floppy_ready\n");
}
#endif
-
if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
disk_change(current_drive) &&
!DP->select_delay)
twaddle(); /* this clears the dcl on certain drive/controller
* combinations */

+#ifdef fd_chose_dma_mode
+ if ((raw_cmd->flags & FD_RAW_READ) ||
+ (raw_cmd->flags & FD_RAW_WRITE))
+ fd_chose_dma_mode(raw_cmd->kernel_data,
+ raw_cmd->length);
+#endif
+
if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
perpendicular_mode();
fdc_specify(); /* must be done here because of hut, hlt ... */
seek_floppy();
- } else
+ } else {
+ if ((raw_cmd->flags & FD_RAW_READ) ||
+ (raw_cmd->flags & FD_RAW_WRITE))
+ fdc_specify();
setup_rw_floppy();
+ }
}

static void floppy_start(void)
@@ -2403,6 +2451,7 @@
#endif
}

+#if 0
static inline int check_dma_crossing(char *start,
unsigned long length, char *message)
{
@@ -2413,6 +2462,7 @@
} else
return 0;
}
+#endif

/*
* Formulate a read/write request.
@@ -2571,9 +2621,9 @@
indirect, direct, sector_t);
return 0;
}
- check_dma_crossing(raw_cmd->kernel_data,
+/* check_dma_crossing(raw_cmd->kernel_data,
raw_cmd->length,
- "end of make_raw_request [1]");
+ "end of make_raw_request [1]");*/
return 2;
}
}
@@ -2619,8 +2669,8 @@
raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
raw_cmd->length <<= 9;
#ifdef FLOPPY_SANITY_CHECK
- check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length,
- "end of make_raw_request");
+ /*check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length,
+ "end of make_raw_request");*/
if ((raw_cmd->length < current_count_sectors << 9) ||
(raw_cmd->kernel_data != CURRENT->buffer &&
CT(COMMAND) == FD_WRITE &&
@@ -3008,6 +3058,8 @@
if (ptr->length <= 0)
return -EINVAL;
ptr->kernel_data =(char*)fd_dma_mem_alloc(ptr->length);
+ fallback_on_nodma_alloc(&ptr->kernel_data,
+ ptr->length);
if (!ptr->kernel_data)
return -ENOMEM;
ptr->buffer_length = ptr->length;
@@ -3578,18 +3630,22 @@
try = 32; /* Only 24 actually useful */

tmp=(char *)fd_dma_mem_alloc(1024 * try);
- if (!tmp) {
+ if (!tmp && !floppy_track_buffer) {
try >>= 1; /* buffer only one side */
INFBOUND(try, 16);
tmp= (char *)fd_dma_mem_alloc(1024*try);
}
- if (!tmp) {
+ if(!tmp && !floppy_track_buffer) {
+ fallback_on_nodma_alloc(&tmp, 2048 * try);
+ }
+ if (!tmp && !floppy_track_buffer) {
DPRINT("Unable to allocate DMA memory\n");
RETERR(ENXIO);
}
- if (floppy_track_buffer)
- fd_dma_mem_free((unsigned long)tmp,try*1024);
- else {
+ if (floppy_track_buffer) {
+ if(tmp)
+ fd_dma_mem_free((unsigned long)tmp,try*1024);
+ } else {
buffer_min = buffer_max = -1;
floppy_track_buffer = tmp;
max_buffer_sectors = try;
@@ -3886,9 +3942,9 @@
{ "silent_dcl_clear", floppy_set_flags, 0, 1, FD_SILENT_DCL_CLEAR },
{ "debug", floppy_set_flags, 0, 1, FD_DEBUG },

- { "nodma", 0, &use_virtual_dma, 1, 0 },
- { "omnibook", 0, &use_virtual_dma, 1, 0 },
- { "dma", 0, &use_virtual_dma, 0, 0 },
+ { "nodma", 0, &can_use_virtual_dma, 1, 0 },
+ { "omnibook", 0, &can_use_virtual_dma, 1, 0 },
+ { "yesdma", 0, &can_use_virtual_dma, 0, 0 },

{ "fifo_depth", 0, &fifo_depth, 0xa, 0 },
{ "nofifo", 0, &no_fifo, 0x20, 0 },
@@ -3937,6 +3993,7 @@

static int have_no_fdc= -EIO;

+
__initfunc(int floppy_init(void))
{
int i,unit,drive;
@@ -3972,6 +4029,7 @@
#endif
}

+ use_virtual_dma = can_use_virtual_dma & 1;
fdc_state[0].address = FDC1;
if (fdc_state[0].address == -1) {
unregister_blkdev(MAJOR_NR,"fd");
@@ -4017,6 +4075,8 @@
FDCS->address = -1;
continue;
}
+ if(can_use_virtual_dma == 2 && FDCS->version < FDC_82072A)
+ can_use_virtual_dma = 0;

have_no_fdc = 0;
/* Not all FDCs seem to be able to handle the version command
@@ -4032,7 +4092,7 @@
initialising=0;
if (have_no_fdc) {
DPRINT("no floppy controllers found\n");
- unregister_blkdev(MAJOR_NR,"fd");
+ unregister_blkdev(MAJOR_NR,"fd");
}
return have_no_fdc;
}
@@ -4063,6 +4123,7 @@
usage_count--;
return -1;
}
+
for (fdc=0; fdc< N_FDC; fdc++){
if (FDCS->address != -1){
if (check_region(FDCS->address, 6) < 0 ||
diff -ur 2.1.78/linux/include/asm-i386/floppy.h linux/include/asm-i386/floppy.h
--- 2.1.78/linux/include/asm-i386/floppy.h Tue Jan 6 19:25:45 1998
+++ linux/include/asm-i386/floppy.h Sun Jan 11 08:18:28 1998
@@ -12,30 +12,42 @@

#include <linux/vmalloc.h>

+
+/*
+ * The DMA channel used by the floppy controller cannot access data at
+ * addresses >= 16MB
+ *
+ * Went back to the 1MB limit, as some people had problems with the floppy
+ * driver otherwise. It doesn't matter much for performance anyway, as most
+ * floppy accesses go through the track buffer.
+ */
+#define _CROSS_64KB(a,s,vdma) \
+(!vdma && ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64))
+
+#define CROSS_64KB(a,s) _CROSS_64KB(a,s,use_virtual_dma & 1)
+
+
#define SW fd_routine[use_virtual_dma&1]
+#define CSW fd_routine[can_use_virtual_dma & 1]
+#define NCSW fd_routine[(can_use_virtual_dma >> 1)& 1]


#define fd_inb(port) inb_p(port)
#define fd_outb(port,value) outb_p(port,value)

-#define fd_enable_dma() SW._enable_dma(FLOPPY_DMA)
-#define fd_disable_dma() SW._disable_dma(FLOPPY_DMA)
-#define fd_request_dma() SW._request_dma(FLOPPY_DMA,"floppy")
-#define fd_free_dma() SW._free_dma(FLOPPY_DMA)
-#define fd_clear_dma_ff() SW._clear_dma_ff(FLOPPY_DMA)
-#define fd_set_dma_mode(mode) SW._set_dma_mode(FLOPPY_DMA,mode)
-#define fd_set_dma_addr(addr) SW._set_dma_addr(FLOPPY_DMA,addr)
-#define fd_set_dma_count(count) SW._set_dma_count(FLOPPY_DMA,count)
+#define fd_request_dma() CSW._request_dma(FLOPPY_DMA,"floppy")
+#define fd_free_dma() CSW._free_dma(FLOPPY_DMA)
#define fd_enable_irq() enable_irq(FLOPPY_IRQ)
#define fd_disable_irq() disable_irq(FLOPPY_IRQ)
-#define fd_cacheflush(addr,size) /* nothing */
-#define fd_request_irq() SW._request_irq(FLOPPY_IRQ, floppy_interrupt, \
- SA_INTERRUPT|SA_SAMPLE_RANDOM, \
- "floppy", NULL)
+#define fd_request_irq() NCSW._request_irq(FLOPPY_IRQ, floppy_interrupt,\
+ SA_INTERRUPT|SA_SAMPLE_RANDOM,\
+ "floppy", NULL)
#define fd_free_irq() free_irq(FLOPPY_IRQ, NULL)
#define fd_get_dma_residue() SW._get_dma_residue(FLOPPY_DMA)
#define fd_dma_mem_alloc(size) SW._dma_mem_alloc(size)
-#define fd_dma_mem_free(addr,size) SW._dma_mem_free(addr,size)
+#define fd_dma_setup(addr, size, mode, io) SW._dma_setup(addr, size, mode, io)
+
+#define FLOPPY_CAN_FALLBACK_ON_NODMA

static int virtual_dma_count=0;
static int virtual_dma_residue=0;
@@ -143,16 +155,13 @@
#endif
}

-static void vdma_enable_dma(unsigned int dummy)
-{
- doing_pdma = 1;
-}
-
-static void vdma_disable_dma(unsigned int dummy)
+static void fd_disable_dma(void)
{
+ if(! (can_use_virtual_dma & 1))
+ disable_dma(FLOPPY_DMA);
doing_pdma = 0;
virtual_dma_residue += virtual_dma_count;
- virtual_dma_count=0;
+ virtual_dma_count=0;
}

static int vdma_request_dma(unsigned int dmanr, const char * device_id)
@@ -164,26 +173,6 @@
{
}

-static void vdma_set_dma_mode(unsigned int dummy,char mode)
-{
- virtual_dma_mode = (mode == DMA_MODE_WRITE);
-}
-
-static void hset_dma_addr(unsigned int no, char *addr)
-{
- set_dma_addr(no, virt_to_bus(addr));
-}
-
-static void vdma_set_dma_addr(unsigned int dummy, char *addr)
-{
- virtual_dma_addr = addr;
-}
-
-static void vdma_set_dma_count(unsigned int dummy,unsigned int count)
-{
- virtual_dma_count = count;
- virtual_dma_residue = 0;
-}

static int vdma_get_dma_residue(unsigned int dummy)
{
@@ -197,7 +186,8 @@
const char *device,
void *dev_id)
{
- return request_irq(irq, floppy_hardint,SA_INTERRUPT,device, dev_id);
+ return request_irq(irq, floppy_hardint,SA_INTERRUPT,device,
+ dev_id);

}

@@ -206,30 +196,74 @@
return __get_dma_pages(GFP_KERNEL,__get_order(size));
}

-static void dma_mem_free(unsigned long addr, unsigned long size)
-{
- free_pages(addr, __get_order(size));
-}

static unsigned long vdma_mem_alloc(unsigned long size)
{
return (unsigned long) vmalloc(size);
+
}

-static void vdma_mem_free(unsigned long addr, unsigned long size)
+#define nodma_mem_alloc(size) vdma_mem_alloc(size)
+
+static void _fd_dma_mem_free(unsigned long addr, unsigned long size)
{
- return vfree((void *)addr);
+ if((unsigned int) addr >= (unsigned int) high_memory)
+ return vfree((void *)addr);
+ else
+ free_pages(addr, __get_order(size));
+}
+
+#define fd_dma_mem_free(addr, size) _fd_dma_mem_free(addr, size)
+
+static void _fd_chose_dma_mode(char *addr, unsigned long size)
+{
+ if(can_use_virtual_dma == 2) {
+ if((unsigned int) addr >= (unsigned int) high_memory ||
+ virt_to_bus(addr) >= 0x1000000 ||
+ _CROSS_64KB(addr, size, 0))
+ use_virtual_dma = 1;
+ else
+ use_virtual_dma = 1;
+ } else {
+ use_virtual_dma = can_use_virtual_dma & 1;
+ }
+}
+
+#define fd_chose_dma_mode(addr, size) _fd_chose_dma_mode(addr, size)
+
+
+static int vdma_dma_setup(char *addr, unsigned long size, int mode, int io)
+{
+ doing_pdma = 1;
+ virtual_dma_port = io;
+ virtual_dma_mode = (mode == DMA_MODE_WRITE);
+ virtual_dma_addr = addr;
+ virtual_dma_count = size;
+ virtual_dma_residue = 0;
+ return 0;
+}
+
+static int hard_dma_setup(char *addr, unsigned long size, int mode, int io)
+{
+#ifdef FLOPPY_SANITY_CHECK
+ if (CROSS_64KB(addr, size)) {
+ printk("DMA crossing 64-K boundary %p-%p\n", addr, addr+size);
+ return -1;
+ }
+#endif
+ /* actual, physical DMA */
+ doing_pdma = 0;
+ clear_dma_ff(FLOPPY_DMA);
+ set_dma_mode(FLOPPY_DMA,mode);
+ set_dma_addr(FLOPPY_DMA,virt_to_bus(addr));
+ set_dma_count(FLOPPY_DMA,size);
+ enable_dma(FLOPPY_DMA);
+ return 0;
}

struct fd_routine_l {
- void (*_enable_dma)(unsigned int dummy);
- void (*_disable_dma)(unsigned int dummy);
int (*_request_dma)(unsigned int dmanr, const char * device_id);
void (*_free_dma)(unsigned int dmanr);
- void (*_clear_dma_ff)(unsigned int dummy);
- void (*_set_dma_mode)(unsigned int dummy, char mode);
- void (*_set_dma_addr)(unsigned int dummy, char *addr);
- void (*_set_dma_count)(unsigned int dummy, unsigned int count);
int (*_get_dma_residue)(unsigned int dummy);
int (*_request_irq)(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
@@ -237,42 +271,26 @@
const char *device,
void *dev_id);
unsigned long (*_dma_mem_alloc) (unsigned long size);
- void (*_dma_mem_free)(unsigned long addr, unsigned long size);
+ int (*_dma_setup)(char *addr, unsigned long size, int mode, int io);
} fd_routine[] = {
{
- enable_dma,
- disable_dma,
request_dma,
free_dma,
- clear_dma_ff,
- set_dma_mode,
- hset_dma_addr,
- set_dma_count,
get_dma_residue,
request_irq,
dma_mem_alloc,
- dma_mem_free
+ hard_dma_setup
},
{
- vdma_enable_dma,
- vdma_disable_dma,
vdma_request_dma,
vdma_nop,
- vdma_nop,
- vdma_set_dma_mode,
- vdma_set_dma_addr,
- vdma_set_dma_count,
vdma_get_dma_residue,
vdma_request_irq,
vdma_mem_alloc,
- vdma_mem_free
+ vdma_dma_setup
}
};

-__inline__ void virtual_dma_init(void)
-{
- /* Nothing to do on an i386 */
-}

static int FDC1 = 0x3f0;
static int FDC2 = -1;
@@ -285,14 +303,7 @@

#define FLOPPY_MOTOR_MASK 0xf0

-/*
- * The DMA channel used by the floppy controller cannot access data at
- * addresses >= 16MB
- *
- * Went back to the 1MB limit, as some people had problems with the floppy
- * driver otherwise. It doesn't matter much for performance anyway, as most
- * floppy accesses go through the track buffer.
- */
-#define CROSS_64KB(a,s) (((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64) && ! (use_virtual_dma & 1))
+#define AUTO_DMA
+

#endif /* __ASM_I386_FLOPPY_H */