#include<bits/stdc++.h>
 
#define fi first
#define se second
#define pb push_back
#define mp make_pair
 
#define all(x) (x).begin() , (x).end()
#define rall(x) (x).rbegin() , (x).rend()
 
#define PII pair<LL,LL>
#define mem(v, x) memset(v, x, sizeof v)
 
#define si(x) scanf("%d", &x)
#define sii(x, y) scanf("%d %d", &x, &y)
#define siii(x, y, z) scanf("%d %d %d", &x, &y, &z)
 
#define sf(x) scanf("%lf", &x)
#define endl '\n'

#define FOR(i , N) for(int i = 0  ; i < N ; i++)
#define FOR1(i , N) for(int i = 1  ; i <= N ; i++)

template <typename T> T six(T &n){n = 0;bool neg = false;char c = getchar();while( c < '0' || c > '9'){if(c == '-') neg = true;c = getchar();}while(c >= '0' && c <= '9'){n = n*10 + c-48;c = getchar();}if(neg) n = ~(n-1);return n;}

using namespace std;
 
typedef long long LL;
 
int SET(int N,int pos){return N=N | (1<<pos);}
int RESET(int N,int pos){return N= N & ~(1<<pos);}
bool CHECK(int N,int pos){return (bool)(N & (1<<pos));}


/*
   D er iterative parlam na
   D te First WA Why???

*/
const int MAX = 100001;

vector<int> G[MAX], topSortOrder;
bool vis[MAX];
int in[MAX];
int vertex, edge;
int ans;

namespace TopologicalSort {
   
   int indegree[MAX];
   vector<int> zeroIndegree;

   void __init() {
      topSortOrder.clear();
      zeroIndegree.clear();
      for(int i = 1; i <= vertex ; i++) {
         indegree[i] = in[i];
         vis[i] = 0;
      }
   }

   void topSort_DFS(int u) { 
      vis[u] = 1;
      if(!zeroIndegree.empty()) zeroIndegree.clear();
      for(int v : G[u]) {
         indegree[v]--;
         if(indegree[v] == 0) zeroIndegree.push_back(v);
      }
      for(int v : zeroIndegree) topSort_DFS(v); 
      topSortOrder.push_back(u);
   }

   bool hasTopSort() { 
      __init();
      for(int i = 1 ; i <= vertex ; i++) {
         if(indegree[i] == 0 && vis[i] == 0) {
            topSort_DFS(i);
         }
      }
      reverse(topSortOrder.begin() , topSortOrder.end());
      return ( (int) topSortOrder.size() == vertex);
   }

   bool topSort_Lexicographical() { 
      __init();
      priority_queue<int> pq;
      for(int i = 1; i <= vertex ; i++) {
         if(indegree[i] == 0) pq.push(-i), vis[i] = 1;
      }

      while(!pq.empty()) {
         int u = -pq.top();
         pq.pop();
         topSortOrder.push_back(u);
         for(int v : G[u]) {
            indegree[v]--;
            if(vis[v] == 0 && indegree[v] == 0) {
               vis[v] = 1;
               pq.push(-v);
            }
         }
      }
      return ( (int) topSortOrder.size() == vertex);
   }

   void topsort_BackTrack() {
      if((int) topSortOrder.size() == vertex) {
         for(int x : topSortOrder) printf("%d ", x);
         printf("\n"); return ;
      }

      for(int i = 1 ; i <= vertex ; i++) {
         if(!vis[i] && !indegree[i]) {
            vis[i] = 1;
            for(int v : G[i]) indegree[v]--;
            topSortOrder.push_back(i);
            topsort_BackTrack();
            topSortOrder.pop_back();
            for(int v : G[i]) indegree[v]++;
            vis[i] = 0;
         }
      }
   }

   void allPossibleTopSort() {
      __init();
      topsort_BackTrack();
   }
}

int main()
{

#ifndef ONLINE_JUDGE // dont use this in toph/codemarshal
    // freopen("input.txt", "r", stdin);
    // freopen("output.txt", "w", stdout);
#endif
   cin >> vertex >> edge;
   FOR(i , edge) {
      int x, y;
      cin >> x >> y;
      G[x].pb(y);
      in[y]++;
   }
   TopologicalSort :: hasTopSort();
   queue<int> q;
   mem(vis, 0);
   int dp[vertex + 10];
   mem(dp, 0);
   FOR1(i , vertex) if(in[i] == 0) {
      vis[i] = 1;
      q.push(i);
   }

   while(!q.empty()) {
      int u = q.front();
      q.pop();
      // cout << u << endl;
      for(int v : G[u]) {
         in[v]--;
         if(in[v] == 0 && !vis[v]) {
            vis[v] = 1;
            dp[v] = max(dp[v], dp[u] + 1);
            ans = max(ans , dp[v]);
            // cout << "Vitore " << v << endl;
            q.push(v);
         }
      }
   }
   cout << ans << endl;
}  


Measured memory_bytes: 13436