Is this normal? Does it just mean throughput is too low to achieve a full buffer on the SBC's side? Baud rate is 115200
It is normal on Linux SBCs at such 'low' baud rates. It is only about 11520 bytes per second, after all, so the kernel has ample time to buffer the incoming data –– there is no buffer per se, but the entire tty layer acts like one ––, and the application handling the data keeps consuming the data fast/often enough so there is no stall. Even on older SBCs like Odroid C1+s and original Raspberry Pis, you need a much higher sustained data flow to start seeing non-clear-to-send state.
How can I be sure this is working normally?
I'd use a simple program on the SBC to send and receive data, and a microcontroller sending and receiving data at the maximum baud rate.
I like to use the Xorshift64* pseudo-random number generator to generate the sequence (two in parallel here), with the SBC providing the random (64-bit unsigned nonzero) seeds, with both generating their own copies using the same two seeds, and comparing to what they receive.
I use similar code to measure the sustained bandwidth over USB Serial, except that because USB 2 is half-duplex, I only send OR receive, not both at the same time.
Reduce the priority of the program running on the SBC, for example down to idle by using
nice -n +19 ionice -c 3 ./program..., and run other stuff on it (representing maximum expected load on the SBC during communications), and see if the SBC and the microcontroller fall out of sync.
You can use any microcontroller you like for such a test, even Arduino Nano, although I prefer to use Teensies. If the SBC uses different logic levels than the microcontroller (Odroid HC1 for example uses 1.8V levels on its UART), I recommend you use a TI TXU0204 level shifter; these have proven to be extremely robust in my use. (I used to use 74LVC1T45 etc. before I stumbled on the TXU0n0m series. Bidirectional level shifters often have a "glitch" in the low state when they sense the direction wrong, and sometimes the change in the low-level signal, while small, can cause issues. I only use either unidirectional ones, ones with a direction pin, or dedicated I2C ones.)
Essentially, this is a practical test for the robustness of the communication channel, with or without hardware handshaking.