C++ inline vs. method

OK, then humor me. What's the 30-years' experience alternative to this typical situation?

int  hwdev_sendmsg (char *data, size_t data_len, uint32_t  timeout) {
    
    uint32_t  now = millis();
    uint8_t  flags;
    int  rval;
    
    // This is timing critical
    flags = SREG;
    cli();
    
    // Initialize hardware
    rval = dev_init();
    
    if (rval != ERR_OK) goto cleanup;
    
    // Set hardware to whatever appropriate mode
    rval = dev_set_mode(SOME_FLAG);
    
    if (rval != ERR_OK) goto cleanup;
    
    while ( ((rval = dev_query()) != ERR_OK) && (data_len != 0) ) {
        
        // Check for timeout
        if ( (rval == ERR_BUSY) && ( (millis() - now) < timeout) ) {
            goto cleanup;
        }
        
        // Check for fatal error
        if (rval == ERR_FATAL) {
            goto cleanup;
        }
       
        // Send data to device
        while (data_len) {
            rval = dev_send8(data);
            
            // Device will signal when its buffer is full
            if (rval == ERR_BUFFER_FULL) {
                // Fall back to wait loop
                break;
            }
            
            // Handle other errors
            if (rval != ERR_OK) {
                goto cleanup;
            }
            
            // Update data pointer and size
            data++;
            data_len--;
        } // inner loop
    } // outer loop
    
    // If data has been sent successfully, rval will be ERR_OK.
    // Otherwise, the relevant error message will be returned.
    
    cleanup:
    
    // We're done with the device now
    dev_close();
    
    // Restore previous state
    SREG = flags;
    
    return rval;
}

This is an anonymous and contrived example -- not a direct copy of a real driver, but close enough to a LOT of loops-within-loops I've had to write recently. (E.g., file read loop with a buffer processing loop within.) The cleanup portion is fairly benign here. In real code, it can easily be half a dozen statements long. You could put those inline anywhere there's a goto above, or use a flag variable and bail out if it's set, or any of a dozen other techniques... But I fail to see what's so gosh-darn diabolical about the obvious and tidy goto approach? It works, the purpose is clear, the code path is trivial to follow, it keeps you from having to write redundant cleanup code (and these loops are fairly short, with few exit points -- there could be a lot more!), and it prevents errors if you have to free memory or restore flags or whatever else you might forget to do otherwise.

A LOT of very real, very thoroughly vetted, production-level code uses this pattern. I cannot imagine, if there were truly something harmful about it, that you would see this in so many ubiquitous codebases -- like the Linux kernel, OpenSSL, Firefox ....

Nonetheless, I am willing to hear the counter-argument if someone feels compelled to plead their case. I don't expect it'll make the wholesale ban any less nonsensical to me, but who knows?