/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author I.E
 */

#include <iostream>
#include <fstream>

#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <math.h>
#include <iomanip>
#include <algorithm>

using namespace std;

#define ll long long
#define ull unsigned long long
#define ld long double
#define fr(i,a,b) for(int i=a;i<b;++i)
#define frn(i,a,b) for(int i=a;i>b;i--)
#define rep(i,n) fr(i,0,n)
#define present(container, element) (container.find(element) != container.end())
#define cpresent(container, element) (find(all(container),element) != container.end())
#define sz(a) int((a).size())
#define srt(container, lambda) sort(container.begin(), container.end(), lambda)

typedef vector<bool> vb;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<vi> vvi;
typedef vector<vl> vvl;
typedef pair<int,int> ii;

int int_pow(int x, int p)
{
    if (p == 0) return 1;
    if (p == 1) return x;

    int tmp = int_pow(x, p/2);
    if (p%2 == 0) return tmp * tmp;
    else return x * tmp * tmp;
}

void fill(istream& in, vi& a, int n) {
    int elm;
    rep(i, n) {
        in >> elm;
        a.push_back(elm);
    }
}

void fill(istream& in, vl& a, int n) {
    ll elm;
    rep(i, n) {
        in >> elm;
        a.push_back(elm);
    }
}

void fill(vi& a, int n, int val) {
    rep(i, n) {
        a.push_back(val);
    }
}

void fill(vl& a, int n, ll val) {
    rep(i, n) {
        a.push_back(val);
    }
}

int sum(vi arr) {
    int s = 0;
    for (auto& x: arr)
        s += x;
    return s;
}

int max(vi a) {
    return *max_element(a.begin(), a.end());
}

int min(vi a) {
    return *min_element(a.begin(), a.end());
}

void print(ostream& out, vi a) {
    for (auto& x: a) {
        out << x << " ";
    }
    out << endl;
}

void print(ostream& out, vvi aa) {
    for (auto& a: aa) {
        print(out, a);
    }
}

void print(ostream& out, vl a) {
    for (auto& x: a) {
        out << x << " ";
    }
    out << endl;
}

//======================================================
int dx[] = { 0,  0, 1, -1 ,  1,  -1,  1,  -1 };
int dy[] = { 1, -1, 0,  0 , -1,   1,  1,  -1 };
//======================================================
ll gcd(ll a, ll b) { return !b ? a : gcd(b, a % b); }
ll lcm(ll a, ll b) { return (a / gcd(a, b)) * b; }
ll power(ull a, ull b, ull ret = 1LL) {
    while (b--) ret = (ret * a);
    return ret;
}
//======================================================

#pragma GCC optimize("Ofast")
static auto speedup = [](){
    ios_base::sync_with_stdio(0);
    cin.tie(nullptr);
    cout.tie(nullptr);
    return 0;
}();



class GLongestPathEditorial {
public:
    void dfs(int i, int& ans, vvi& adj, vb& seen, vi& ds) {
        if (seen[i]) return;
        seen[i] = true;
        int l = adj[i].size();
        rep(j, l) {
            int vertex = adj[i][j];
            dfs(vertex, ans, adj, seen, ds);
            ds[i] = max(ds[i], ds[vertex] + 1);
        }
        ans = max(ans, ds[i]);
    }
	void solve(std::istream& in, std::ostream& out) {
	    int n, m;
	    in >> n >> m;
	    vi a;
	    vvi adj(n+1, a);
	    rep(i, m) {
	        int start, end;
	        in >> start >> end;
	        adj[start].push_back(end);
	    }
	    int ans = -1;
	    vb seen(n+1, false);
	    vi ds(n+1, 0);
	    fr(i, 1, n+1) {
	        dfs(i, ans, adj, seen, ds);
	    }
	    out << ans << endl;
	}
};


int main() {
	GLongestPathEditorial solver;
	std::istream& in(std::cin);
	std::ostream& out(std::cout);
	solver.solve(in, out);
	return 0;
}


Measured memory_bytes: 12776