There are three ways you can handle flow control:
- If you're overloaded, you drop data on the floor.
- If you can't provide service for a request from a higher layer, typically because your local queue is full, you return an error to that higher layer.
- You proactively notify higher layers that they need to slow down.
At the Ethernet layer, method 3 is supported through pause frames. Often higher layers don't support method 3, but instead support method 2. When a layer has a layer below it that supports method 3 but the layer above it only supports method 2, it can stop passing data to lower layers temporarily, causing method 2 to apply to higher layers.
Or, to put it more concretely, when you receive a pause frame you stop your sending engine and set a timer to restart the sending engine in the appropriate time. While the sending engine is stopped, your local queues will fill up with data from higher layers. If they get full, you return "busy" errors to the higher layers and they handle that however is appropriate.