Let me add some comments to your code:
import torch

def get_hook(msg):
    def hk(x):
        print("Does {}'s grad requires grad?".format(msg))
        print(x.requires_grad)
    return hk

x = torch.randn(16, 5, requires_grad=True)
x.register_hook(get_hook("x"))
w = torch.randn(5, 3, requires_grad=True)
w.register_hook(get_hook("w"))

s = torch.mm(x,w)
s.register_hook(get_hook("s"))

y = s.sum()
# use retain_graph=True to be able to call backward multiple times
y.backward(retain_graph=True)
print("All 3 hooks are called and all print False")

# Asking for graph to be created and set the grad output to requires_grad=True
go = torch.ones((), requires_grad=True)
y.backward(go, create_graph=True, retain_graph=True)
print("All 3 hooks are called and all print True")

# Asking for graph to be created but the grad output has requires_grad=False
y.backward(create_graph=True, retain_graph=True)
print("All 3 hooks are called and s print False but x,w print True")
# This is because go does not require gradients, so the gradient of s doesn't either
# But in the mm, x is used to compute the gradient of w, so the gradient of w requires gradients.