integer N,M
integer,allocatable,dimension(:)::X,Y
integer,allocatable,dimension(:)::DP
integer ans
!読み込み
read*,N,M
allocate(X(m),Y(m))
allocate(DP(N))

do i=1,M
  read*,X(i),Y(i)
end do
!読み込み終了
call insertionsort(x)

DP = -1
!DP[i]とはノードiからのびる経路のうち最短のもの
!まだ調べていない状態は-1とする
ans = 0
do i=1,N
  if(DP(i)==-1)then
    DP(i)=DFS(i,N,X,Y)
    ans=max(ans,DP(i) )
  endif
end do

print"(I0)",ans

contains

recursive function DFS(u,N,X,Y)result(res)
integer u,res
integer N
integer,dimension(:)::X,Y
  if(DP(u) >= 0)then
    res=DP(u)
  else
    DP(u)=0
    do j=1,size(x)
      if(x(j)==u)then
      DP(u)=max(DP(u),DFS(y(j),N,X,Y)+1)
      endif
      if(x(j)>u)exit
    end do
  res=DP(u)
  endif
end function DFS

subroutine insertionsort(s)
  implicit none
  integer, intent(inout) :: s(:)
  integer :: i, j, tmp
  do i = 2, size(s)  ! ループ終了時に [1, i] の範囲が昇順
    do j = i, 2, -1  ! i の位置にあった新要素が適切な位置に来るまで落ちてくる
      if (s(j) >= s(j - 1)) exit
      tmp = s(j)
      s(j) = s(j - 1)
      s(j - 1) = tmp
      tmp = y(j)
      y(j) = y(j-1)
      y(j-1) = tmp
    end do
  end do
end subroutine insertionsort
end