#include <bits/stdc++.h>
using namespace std;
typedef long long           ll;
typedef unsigned long long  ull;
typedef long double         ld;
typedef pair<int, int>      pii;
typedef pair<ll,ll>         pll;
typedef vector<int>         vi;
typedef vector<pii>         vii;
typedef vector<vi >         vvi;
typedef vector<pll>         vlll;
typedef set<int>            si;
typedef map<string, int>    msi;
typedef map<int, int>       mii;
typedef map<ll, ll>         mll;
typedef map<char, int>      mci;
#define rep(i,a,b) for(int i=a;i<b;i++)
#define all(v) v.begin(),v.end()
#define allr(v) v.rbegin(),v.rend()
#define pb emplace_back
#define mp make_pair
#define vi vector<int>
#define hey(i) cout<<"hey"<<" "<<i<<endl;
#define vll vector<ll>
#define file freopen("input.txt","r",stdin);freopen("output.txt","w",stdout);
#define endl "\n"
#define ff first
#define ss second
# define PI 3.14159265358979323846
#define bit(x,j) ((x>>j)&1)
#define sz(a) ll((a).size())
#define PRINT(v) for(auto t:v) cout<<t<<" ";cout<<endl;
#define PRINTM(mpp) for(auto t:mpp) cout<<t.ff<<" "<<t.ss<<endl;
const ll MOD=1e9+7 ;
ll gcdEx(ll a, ll b, ll& x, ll& y) {x = 1, y = 0; ll x1 = 0, y1 = 1, a1 = a, b1 = b;while(b1){ ll q = a1 / b1;tie(x, x1) = make_tuple(x1, x - q * x1); tie(y, y1) = make_tuple(y1, y - q * y1);
tie(a1, b1) = make_tuple(b1, a1 - q * b1);} return a1;}
ll modI(ll a,ll m){ll x, y; ll g = gcdEx(a, m, x, y);if(g != 1) return -1; else return(x % m + m) % m;}
ll modD(ll a, ll b,ll m=MOD){return (modI(b,m)*(a%m))%m;}
ll modS(ll a, ll b,ll m=MOD){return ((a%m)-(b%m)+m)%m;}
ll modP(ll x, ll y,ll m=MOD){ll r=1; x%=m; while(y>0){if(y&1){r=(r*x)%m;} y=y>>1; x=(x*x)%m;} return r;}
ll modA(ll a, ll b,ll m=MOD){return ((a%m)+(b%m))%m;}
ll modM(ll a, ll b,ll m=MOD){return ((a%m)*(b%m))%m;}
ll calc(auto &v){ll sum=0;rep(i,0,v.size()){sum+=v[i];}return sum;}
int factmod(int n, int p) {int res = 1;while (n > 1) {res = (res * ((n/p) % 2 ?  p-1 : 1)) % p;for (int i = 2; i <= n%p; ++i)res = (res * i) % p;n /= p;}
return res % p;}
long long int nCrMod(ll n, ll r,ll p){if (r==0) return 1;ll fac[n+1];fac[0] = 1;for(ll i=1 ; i<=n; i++) fac[i] = (fac[i-1]*i)%p;
return (fac[n]* modI(fac[r], p) % p * modI(fac[n-r], p) % p) % p;}
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a; }
ll lcm(vll &arr){ll ans = arr[0];ll n=(ll)arr.size();for (int i = 1; i < n; i++) ans = (((arr[i] * ans)) /(gcd(arr[i], ans)));return ans;}
ll sumofdigits(ll num){ll ans = 0;while (num){ ans += num % 10;num /= 10;} return ans;}
vll divisors(ll n){set<ll> s;for (ll i=1; i<=sqrt(n); i++){if (n%i==0){if(n/i == i) s.insert(i); else{ s.insert(i);s.insert(n/i);}}} vll v(all(s)); return v;}
//To find if any solution of linear diophantine equation ax+by=c  exists
bool find_any_solution(ll a,ll b,ll c, ll &x0, ll &y0, ll &g) {g = gcdEx(abs(a), abs(b), x0, y0);if (c % g) return false; x0 *= c / g;
    y0 *= c / g; if (a < 0) x0 = -x0;if(b < 0) y0 = -y0;return true;}
vector<ll> trial_division3( ll n) {
    vector<ll> factorization;
    for (ll d : {2, 3, 5}) {
        while (n % d == 0) {
            factorization.push_back(d);
            n /= d;
        }
    }
    static array<int, 8> increments = {4, 2, 4, 2, 4, 6, 2, 6};
    int i = 0;
    for (long long d = 7; d * d <= n; d += increments[i++]) {
        while (n % d == 0) {
            factorization.push_back(d);
            n /= d;
        }
        if (i == 8)
            i = 0;
    }
    if (n > 1)
        factorization.push_back(n);
    return factorization;
}
vll decimaltob(ll n,ll b){
    vll a;
    if(n==0){
        a.pb(0);
        return a;
    }
    for(int i=0; n>0; i++){
        a.pb(n%b);
        n= n/b;
    }
    reverse(all(a));
    return a;
}
void prime(int N,vi &lp){ vi pr;for (int i=2; i<=N; ++i){if (lp[i] == 0) {lp[i] = i;pr.push_back (i);}
for(int j=0; j<(int)pr.size() && pr[j]<=lp[i] && i*pr[j]<=N; ++j) lp[i * pr[j]] = pr[j];}}
void dfs(int v,vvi &adj,vi &visited,vi &ans,vi &color,bool &cycle){color[v]=1;visited[v] = true;for (int u : adj[v]){ if (!visited[u]) dfs(u,adj,visited,ans,color,cycle); else if(color[u]==1) cycle=true;}
color[v]=2;ans.push_back(v);}
vi topological_sort(vvi &adj,int n){ vi visited;visited.assign(n+1, false);vi ans(n+1);vi color(n+1,0);bool cycle=false;for (int i = 1; i <=n; ++i){ if (!visited[i]) dfs(i,adj,visited,ans,color,cycle);}reverse(ans.begin(), ans.end());ans.erase(find(all(ans),0),ans.end());if(cycle) return vi(0); return ans;}
void multi_dfs(int v,vvi &adj, vi &time_in, vi &time_out,vi &color,vi &sub_size,int &dfs_timer,vi &length) {
    //Copy this while calling this multipurpose dfs function
    /*      vi time_in(n+1),time_out(n+1),color(n+1),sub_size(n+1),length(n+1);
            int dfs_timer=0;
            dfs(ROOT,adj,time_in,time_out,color,sub_size,dfs_timer,length);
    */
    // Replace root with the node to begin dfs with
    //sub_size calculates size of subtree of each node while length calculates depth of the tree (in normal graphs calculates number of nodes on longest path)
    //time_in and time_out help in case of determining whether a node i is ancestor of j in O(1)
   time_in[v] = dfs_timer++;
    sub_size[v]=1;
    length[v]=1;
    color[v] = 1;
    for (int u : adj[v]){
        if (color[u] == 0){
            multi_dfs(u,adj,time_in,time_out,color,sub_size,dfs_timer,length);
        }
            sub_size[v]+=sub_size[u];
            length[v]=max(length[v],length[u]+1);
        }
    color[v] = 2;
    time_out[v] = dfs_timer++;
}
vi bfs(vector<vector<int> >&adj, int s, int n){
    queue<int> q;
    vector<bool> used(n+1);
    vector<int> d(n+1), p(n+1);
    q.push(s);
    used[s] = true;
    p[s] = -1;
    while (!q.empty()) {
        int v = q.front();
        q.pop();
        for (int u : adj[v]) {
            if (!used[u]) {
                used[u] = true;
                q.push(u);
                d[u] = d[v] + 1;
                p[u] = v;
            }
        }
}
    return d;
//To restore shortest path from source to some vertex u
 /*   if (!used[u]) {
    cout << "No path!";
}
    else {
        vector<int> path;
        for (int v = u; v != -1; v = p[v])
            path.push_back(v);
        reverse(path.begin(), path.end());
        cout << "Path: ";
        for (int v : path)
            cout << v << " ";
    }*/
    }

void solve(){
    int n,m;
    cin>>n>>m;
    vvi adj(n+1);
    rep(i,0,m){
        int a,b;
        cin>>a>>b;
        adj[a].pb(b);}

    vi time_in(n+1),time_out(n+1),color(n+1),sub_size(n+1),length(n+1);
    int dfs_timer=0;
    rep(i,1,n+1){
    if(color[i]==0)
        multi_dfs(i,adj,time_in,time_out,color,sub_size,dfs_timer,length);
    }
    cout<<*max_element(all(length))-1<<endl;

}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.precision(17);
    int t;
    //cin>>t;
    //while(t--)
    solve();

    return 0;
}


Measured memory_bytes: 15352