I had the feeling, that accessing single pixels in Pillow using all those nested loops isn't the best idea performance-wise.
So, my idea would be to set up a small (m, n) checker using (nested) lists, build a Pillow Image object from that using putdata, and simply resize it to the desired size using the Image.NEAREST resampling filter. I added some flexibility for choosing colors and image modes.
from itertools import chain
from math import ceil
from PIL import Image
m, n = (5, 5) # Checker dimension (x, y)
w, h = (100, 100) # Final image width and height
c1 = 0 # (224, 64, 64) # First color
c2 = 255 # (128, 128, 128) # Second color
mode = 'L' if isinstance(c1, int) else 'RGB' # Mode from first color
# Generate pixel-wise checker, even x dimension
if m % 2 == 0:
pixels = [[c1, c2] for i in range(int(m/2))] + \
[[c2, c1] for i in range(int(m/2))]
pixels = [list(chain(*pixels)) for i in range(ceil(n/2))]
# Generate pixel-wise checker, odd x dimension
else:
pixels = [[c1, c2] for i in range(ceil(m*n/2))]
# Generate final Pillow-compatible pixel values
pixels = list(chain(*pixels))[:(m*n)]
# Generate Pillow image from pixel values, resize to final image size, and save
checker = Image.new(mode, (m, n))
checker.putdata(pixels)
checker = checker.resize((w, h), Image.NEAREST)
checker.save('checker.png')
For the shown configuration, the output would be:

Switching to RGB, and altering m, w, and h, we might get something like this:

As already pointed out in the other answers, if w or h are not whole integer factors of m or n, then you'll get somehow distorted output (m = n = 5, w = h = 102):

----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
Pillow: 8.1.2
----------------------------------------