import sys
from collections import deque
import copy
import math
def get_read_func(fileobject):
    if fileobject == None :
        return raw_input
    else:
        return fileobject.readline

global score

def dfs(graph, nd, d):
    global score
    max_depth = 0
    if score[nd] != -1:
        return score[nd] + d
    if len(graph[nd]) == 0:
        score[nd] = 0
        return 1
    for adj in graph[nd]:
        max_depth = max(max_depth, dfs(graph, adj, d + 1)) - d

    if max_depth > score[nd]:
        score[nd] = max_depth
    return score[nd] + 1


def main():
    if len(sys.argv) > 1:
        f = open(sys.argv[1])
    else:
        f = None
    read_func = get_read_func(f);
    input_raw = read_func().strip().split()
    [N, M] = [int(input_raw[0]), int(input_raw[1])]
    graph = {}
    global score
    score = {}
    for i in range(1, N+1):
        graph[i] = []
        score[i] = -1
    for i in range(M):
        input_raw = read_func().strip().split()
        [a, b] = [int(input_raw[0]), int(input_raw[1])]
        graph[a].append(b)

    max_score = 0
    for i in range(1, N + 1):
        max_score = max(max_score, dfs(graph, i, 0))
    print max_score
##        q = [i]
##        d = 0
##        while 1:
##            if len(q) <= 0:
##                score[i] = d
##                break
##            nd = q.pop()
##            if score[nd] != -1:
##                score[i] = d + score[nd]
##                break
##            for adj in graph[nd]:
##                if score[nd] + 1 > score[adj]:
##                    q.append(adj)
##                    score[adj] = score[nd] + 1
##                else:







if __name__ == '__main__':
    main()


### FIXED CODE ###

import sys
from collections import deque
import copy
import math
def get_read_func(fileobject):
    if fileobject == None :
        return raw_input
    else:
        return fileobject.readline

global score

def dfs(graph, nd, d):
    global score
    max_depth = 0
    if score[nd] != -1:
        return score[nd] + 1
    if len(graph[nd]) == 0:
        score[nd] = 0
        return 1
    for adj in graph[nd]:
        max_depth = max(max_depth, dfs(graph, adj, d + 1))

    #if max_depth > score[nd]:
    score[nd] = max_depth
    return score[nd] + 1


def main():
    if len(sys.argv) > 1:
        f = open(sys.argv[1])
    else:
        f = None
    sys.setrecursionlimit(100000)
    read_func = get_read_func(f);
    input_raw = read_func().strip().split()
    [N, M] = [int(input_raw[0]), int(input_raw[1])]
    graph = {}
    global score
    score = {}
    for i in range(1, N+1):
        graph[i] = []
        score[i] = -1
    for i in range(M):
        input_raw = read_func().strip().split()
        [a, b] = [int(input_raw[0]), int(input_raw[1])]
        graph[a].append(b)

    for i in range(1, N + 1):
        dfs(graph, i, 0)
    max_score = 0
    for i in range(1, N + 1):
        max_score = max(max_score, score[i])
    print max_score
##        q = [i]
##        d = 0
##        while 1:
##            if len(q) <= 0:
##                score[i] = d
##                break
##            nd = q.pop()
##            if score[nd] != -1:
##                score[i] = d + score[nd]
##                break
##            for adj in graph[nd]:
##                if score[nd] + 1 > score[adj]:
##                    q.append(adj)
##                    score[adj] = score[nd] + 1
##                else:







if __name__ == '__main__':
    main()