{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad
import Data.Int
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as VM
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as UM
import qualified Data.ByteString.Char8 as BS
import qualified Data.IntSet as IntSet

topologicalSort :: V.Vector IntSet.IntSet -> V.Vector IntSet.IntSet -> U.Vector Int
topologicalSort !edges_from edges_to = U.create $ do
  vec <- UM.new n
  edges_to' <- V.thaw edges_to
  go vec 0 edges_to' IntSet.empty
  return vec
  where
    n = V.length edges_from
    go !vec !i !edges_to !seen
      | i >= n = return ()
      | otherwise = do
          x:_ <- filterM (\x -> do
                             set <- VM.read edges_to x
                             return (IntSet.null set && x `IntSet.notMember` seen)) [0..n-1]
          UM.write vec i x
          forM_ (IntSet.toList (edges_from V.! x)) $ \i -> do
            set <- VM.read edges_to i
            VM.write edges_to i $! IntSet.delete x set
          go vec (i+1) edges_to (IntSet.insert x seen)

main = do
  [n,m] :: [Int] <- map (read . BS.unpack) . BS.words <$> BS.getLine
  -- 2 <= n <= 10^5, 1 <= m <= 10^5
  edges :: U.Vector (Int, Int) <- U.replicateM m $ do
    [x,y] <- map (read . BS.unpack) . BS.words <$> BS.getLine
    -- 1 <= x, y <= n
    return (x - 1, y - 1 :: Int)
  let edges_to :: V.Vector IntSet.IntSet
      edges_to = V.create $ do
        sets <- VM.replicate n IntSet.empty
        U.forM_ edges $ \(x,y) -> do
          VM.modify sets (IntSet.insert x) y
        return sets
  let edges_from :: V.Vector IntSet.IntSet
      edges_from = V.create $ do
        sets <- VM.replicate n IntSet.empty
        U.forM_ edges $ \(x,y) -> do
          VM.modify sets (IntSet.insert y) x
        return sets
      sorted = topologicalSort edges_from edges_to
  let result :: U.Vector Int
      result = U.create $ do
        vec <- UM.new n
        U.forM_ sorted $ \x -> do
          m <- foldM (\ !m y -> do t <- UM.read vec y
                                   return $! max m (t + 1)) 0 (IntSet.toList (edges_to V.! x))
          UM.write vec x m
        return vec
  -- print sorted
  -- print result
  print (U.maximum result)
