// YOU THINK YOU KNOW ME.//


#include<bits/stdc++.h>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
//using namespace __gnu_pbds;
using namespace std;
#define int                      long long
#define MOD                     1000000007
#define inf                     9e18
#define ld                      long double
#define countbit                __builtin_popcount
#define loop(i,a,n)				for(int i=a;i<n;i++)
#define pb                      push_back
#define pf                      push_front
#define mp                      make_pair
#define f                       first
#define s                       second
#define isvowel(a)              (a == 'a' || a == 'e' || a == 'i' || a == 'o' || a == 'u')
#define boost                   ios_base::sync_with_stdio();cin.tie(0);cout.tie(0);
#define cases                   int tests;cin>>tests;while(tests--)
#define all(s)                  s.begin(),s.end()
#define line                    cout<<endl;
#define watch(x)                cout<<(#x)<<" is "<<x<<endl
//typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> rich_set;
bool isprime(int n){if(n==1)return false;if(n==2)return true;for(int i=2;i<=sqrt(n);i++){if(n%i==0){ return false;}}return true;}
int poww(int b,int e){if(e==0)return 1;else if(e%2==0){int a=pow(b,e/2);return a*a;}else {int a=pow(b,e/2);return b*a*a;}}
int powm(int x,int y,int m=MOD){x=x%m;int res=1;while(y){if(y&1)res=res*x;res%=m;y=y>>1;x=x*x;x%=m;}return res;}
int modInverse(int a, int m=MOD) { return powm(a, m-2, m);}
void print(vector<int>v){for(int i=0;i<v.size();i++){cout<<v[i]<<" ";}}
void print(int arr[]){int size = *(&arr + 1) - arr; for(int i=0;i<size;i++){cout<<arr[i]<<" ";}}
string to_bin(int n){string res="";int i=0;while(n>0){if(n%2==1){res=res+'1';}else{res=res+'0';}n/=2;i++;}reverse(all(res));return res;}
int to_dec(string s){int n=s.size(),ans=0,temp=1;for(int i=n-1; i>=0; i--){int x=s[i]-'0';ans+=x*temp;temp*=2;}return ans;}
void result(bool f){if(f)cout<<"YES"<<endl;else cout<<"NO"<<endl;}

int dp[100005];

void addEdge(vector<int> adj[], int u, int v) 
{ 
    adj[u].push_back(v); 
   // adj[v].push_back(u); 
} 
  
// A utility function to do DFS of graph 
// recursively from a given vertex u. 
int DFSUtil(int u, vector<int> adj[], 
                    vector<bool> &visited) 
{ 
    if(visited[u]){
        return dp[u];
    }
    visited[u] = true; 
    //cout << u << " ";
    int val=0; 
    for (int i=0; i<adj[u].size(); i++) 
      //  if (visited[adj[u][i]] == false) 
            val= max(val,DFSUtil(adj[u][i], adj, visited)+1ll); 
        
    dp[u]=val;
    return val;
} 
  
// This function does DFSUtil() for all  
// unvisited vertices. 
void DFS(vector<int> adj[], int V) 
{ 
    vector<bool> visited(V, false); 
    for (int u=1; u<=V; u++) 
        if (visited[u] == false) 
            DFSUtil(u, adj, visited); 
} 


void solve(){
	int n;
    cin>>n;
    int m;
    cin>>m;
    vector<int> gr[n+1];
    loop(i,0,m){
        int a,b;
        cin>>a>>b;
        addEdge(gr,a,b);
    }
    DFS(gr,n);
    int ans=-1;
    loop(i,1,n+1){
        ans= max(ans,dp[i]);
    }
    cout<<ans<<endl;

}
int32_t main(){
	boost
  //cases
	solve();	
	
	//return 0;
}


Measured memory_bytes: 9216