Earlier today I was stress-testing a SerialPort component for Nito.Async when I ran into an unusual error: ERROR_NO_SYSTEM_RESOURCES (1450).
This error can be caused by exhausting any of several OS resources, though all the examples I’ve found deal with exhausing memory-related resources. In my particular example, I was trying to shove a 600 MB file across a serial port all at once.
There’s a limit to how big of a user-mode buffer one can send to a device driver (so this comes into play if you’re talking to a device, such as a serial port or named pipe; it also affects I/O to regular files if FILE_FLAG_NO_BUFFERING was used). According to Dan Moseley of Microsoft, the basis of this limitation is in how the I/O Manager creates its memory descriptor list (MDL).
I’m in a position where I will need to transfer large amounts of data over serial ports, so I wanted to know how much data can be transferred in a single call. Dan Moseley’s original description updated with the IoAllocateMdl MSDN docs, along with the page size information from the latest revision of Windows Internals was enough information to calculate the answer, which I’ve summarized below.
|Operating System||Architecture||Page Size||Calculation||Maximum I/O Buffer Size|
|2K/XP/2K3||x86||4096||PAGE_SIZE * (65535 - sizeof(MDL)) / sizeof(ULONG_PTR)||67076096 bytes (63.97 MB)|
|XP/2K3||x64||4096||PAGE_SIZE * (65535 - sizeof(MDL)) / sizeof(ULONG_PTR)||33525760 bytes (31.97 MB)|
|2K/XP/2K3||IA-64||8192||PAGE_SIZE * (65535 - sizeof(MDL)) / sizeof(ULONG_PTR)||67051520 bytes (63.95 MB)|
|Vista/2K8||x86 & x64||4096||(2 GB - PAGE_SIZE)||2147479552 bytes (1.999996 GB)|
|Vista/2K8||IA-64||8192||(2 GB - PAGE_SIZE)||2147479552 bytes (1.999992 GB)|
|Win7/2K8R2||x86 & x64||4096||(4 GB - PAGE_SIZE)||4294963200 bytes (3.999996 GB)|
|Win7/2K8R2||IA-64||8192||(4 GB - PAGE_SIZE)||4294959104 bytes (3.999992 GB)|
The lowest entry here is for XP/2K3 running on x64. So, if 64-bit XP is important, then you should not use I/O buffers over ~31 MB. If you ignore 64-bit XP, then you can use I/O buffers up to ~63 MB. Newer operating systems take great strides towards removing this limitation completely.
Note that this table only applies to the buffer passed to a single API call. There are other I/O-related restrictions; in particular, I cannot simply split up my 600 MB file into 16 MB chunks and still send them all at once; the serial port will not be able to keep up with the requests and will eventually run into another limitation (with the same error code, ERROR_NO_SYSTEM_RESOURCES (1450)). The solution is to implement buffering in the application.