from sys import stdin, setrecursionlimit

from collections import defaultdict

input = stdin.readline

setrecursionlimit(200000)

N, M = list(map(int, input().split()))

# d = [0] * (N + 1)

adj = defaultdict(list)

for _ in range(M):

    x, y = list(map(int, input().split()))

    adj[x].append(y)

    # d[y] += 1



dp = [-1] * (N + 1)





def lp(x: 'int') -> 'int':

    if dp[x] >= 0:

        return dp[x]

    else:

        dp[x] = 0

        for y in adj[x]:

            dp[x] = max(dp[x], lp(y) + 1)

        return dp[x]





print((max(lp(i) for i in range(1, N + 1))))

"""

https://atcoder.jp/contests/dp/tasks/dp_g



Using list to store adjacent list is faster than using sets.

Maybe it is because the append operation of list in python is faster than add operation of sets.



Using defaultdict instead of list to store adj is faster. Maybe it is because in case the graph is

sparse, defauldict can save more query time.

"""

from sys import stdin

from collections import defaultdict, deque

input = stdin.readline

N, M = list(map(int, input().split()))

d = [0] * (N + 1)

adj = defaultdict(list)

for _ in range(M):

    x, y = list(map(int, input().split()))

    adj[x].append(y)

    d[y] += 1



dp = [0] * (N + 1)  # dp denotes longest path ends at i



Q = deque([i for i in range(1, N + 1) if d[i] == 0])



while Q:

    cur = Q.popleft()

    for nxt in adj[cur]:

        dp[nxt] = max(dp[nxt], dp[cur] + 1)

        d[nxt] -= 1

        if d[nxt] == 0:

            Q.append(nxt)

print((max(dp)))

from sys import stdin, setrecursionlimit
from collections import defaultdict

input = stdin.readline
setrecursionlimit(200000)
N, M = list(map(int, input().split()))
# d = [0] * (N + 1)
adj = defaultdict(list)
for _ in range(M):
    x, y = list(map(int, input().split()))
    adj[x].append(y)
    # d[y] += 1
dp = [-1] * (N + 1)


def lp(x: "int") -> "int":
    if dp[x] >= 0:
        return dp[x]
    else:
        dp[x] = 0
        for y in adj[x]:
            dp[x] = max(dp[x], lp(y) + 1)
        return dp[x]


print((max(lp(i) for i in range(1, N + 1))))

"""
https://atcoder.jp/contests/dp/tasks/dp_g
Using list to store adjacent list is faster than using sets.
Maybe it is because the append operation of list in python is faster than add operation of sets.
Using defaultdict instead of list to store adj is faster. Maybe it is because in case the graph is
sparse, defauldict can save more query time.
"""
from sys import stdin
from collections import defaultdict, deque

input = stdin.readline
N, M = list(map(int, input().split()))
d = [0] * (N + 1)
adj = defaultdict(list)
for _ in range(M):
    x, y = list(map(int, input().split()))
    adj[x].append(y)
    d[y] += 1
dp = [0] * (N + 1)  # dp denotes longest path ends at i
Q = deque([i for i in range(1, N + 1) if d[i] == 0])
while Q:
    cur = Q.popleft()
    for nxt in adj[cur]:
        dp[nxt] = max(dp[nxt], dp[cur] + 1)
        d[nxt] -= 1
        if d[nxt] == 0:
            Q.append(nxt)
print((max(dp)))

-from sys import stdin, setrecursionlimit -from collections import defaultdict +""" +https://atcoder.jp/contests/dp/tasks/dp_g +Using list to store adjacent list is faster than using sets. +Maybe it is because the append operation of list in python is faster than add operation of sets. +Using defaultdict instead of list to store adj is faster. Maybe it is because in case the graph is +sparse, defauldict can save more query time. +""" +from sys import stdin +from collections import defaultdict, deque -setrecursionlimit(200000) -# d = [0] * (N + 1) +d = [0] * (N + 1) -    # d[y] += 1 -dp = [-1] * (N + 1) - - -def lp(x: "int") -> "int": -    if dp[x] >= 0: -        return dp[x] -    else: -        dp[x] = 0 -        for y in adj[x]: -            dp[x] = max(dp[x], lp(y) + 1) -        return dp[x] - - -print((max(lp(i) for i in range(1, N + 1)))) +    d[y] += 1 +dp = [0] * (N + 1)  # dp denotes longest path ends at i +Q = deque([i for i in range(1, N + 1) if d[i] == 0]) +while Q: +    cur = Q.popleft() +    for nxt in adj[cur]: +        dp[nxt] = max(dp[nxt], dp[cur] + 1) +        d[nxt] -= 1 +        if d[nxt] == 0: +            Q.append(nxt) +print((max(dp)))