It looks mostly functional to me.
A couple of remarks…
First remark: Your data X_batch seems to be of shape (batches, timesteps, features) so I assume that your RNN units are initialised with batch_first=True otherwise you will get funky results.
With X_batch of shape (batches, timesteps, features), sub_sample will not be contiguous so either PyTorch will throw an error complaining about the data being non-contiguous or PyTorch automatically copies the data when you wrap it in a Variable. Basically, if your code runs then it will run inefficiently.
It would be better if X_batch and y_batch were of shape (timesteps, batches, features) because then …
sub_sample = X_batch[s_idx:s_idx + sub_step] # slicing over the time dimension

will create slices of contiguous data and PyTorch won’t have to copy the data to make the Variables contiguous.
Second remark: The line _ = model(sample_v) runs the model on the same input data for a second time, but the model.hidden state has already been updated. So this is unnecessary and will mess with the model’s memory of the past.
I would suggest dispensing with model.last_hidden and maybe using .detach_() instead of repackaging the hidden state
for for s_idx in start_idx:
    # prepare sample_v and target_v
    for h in model.hidden:
        h.detach_() # tells PyTorch not to backpropagate furthur back into the past
    # model.hidden = [Variable(h.data) for h in model.hidden] # would work too
    net_output = model(sample_v)
    loss = loss_function(net_output, target_v)
    model.zero_grad()
    loss.backward()
    optimizer.step()
    # don't rerun the model on sample_v
    # no need to save model.last_hidden